<?php

namespace App\Services;

use App\Models\Company;
use App\Models\SubscriptionCoupon;
use App\Models\SubscriptionCouponRedemption;
use App\Models\SubscriptionPlan;
use Illuminate\Validation\ValidationException;

class CouponService
{
    public function findActiveByCode(string $code): ?SubscriptionCoupon
    {
        $normalized = strtoupper(trim($code));
        if ($normalized === '') {
            return null;
        }

        return SubscriptionCoupon::query()
            ->where('code', $normalized)
            ->where('is_active', true)
            ->first();
    }

    public function validate(
        string $code,
        ?Company $company,
        SubscriptionPlan $plan,
        float $amountAfterPlanDiscount
    ): SubscriptionCoupon {
        $coupon = $this->findActiveByCode($code);
        if (! $coupon) {
            throw ValidationException::withMessages([
                'coupon_code' => 'Invalid coupon code.',
            ]);
        }

        if ($coupon->valid_from && $coupon->valid_from->isFuture()) {
            throw ValidationException::withMessages([
                'coupon_code' => 'This coupon is not active yet.',
            ]);
        }

        if ($coupon->valid_until && $coupon->valid_until->isPast()) {
            throw ValidationException::withMessages([
                'coupon_code' => 'This coupon has expired.',
            ]);
        }

        if ($coupon->max_uses !== null && $coupon->used_count >= $coupon->max_uses) {
            throw ValidationException::withMessages([
                'coupon_code' => 'This coupon has reached its usage limit.',
            ]);
        }

        if (! $coupon->appliesToPlan($plan)) {
            throw ValidationException::withMessages([
                'coupon_code' => 'This coupon does not apply to the selected plan.',
            ]);
        }

        if ($coupon->min_amount !== null && $amountAfterPlanDiscount < (float) $coupon->min_amount) {
            throw ValidationException::withMessages([
                'coupon_code' => 'Order amount is below the coupon minimum.',
            ]);
        }

        if ($company && $coupon->max_uses_per_company) {
            $usedByCompany = SubscriptionCouponRedemption::query()
                ->where('subscription_coupon_id', $coupon->id)
                ->where('company_id', $company->id)
                ->count();

            if ($usedByCompany >= (int) $coupon->max_uses_per_company) {
                throw ValidationException::withMessages([
                    'coupon_code' => 'This coupon has already been used for your company.',
                ]);
            }
        }

        return $coupon;
    }

    public function discountAmount(SubscriptionCoupon $coupon, float $amount): float
    {
        if ($amount <= 0) {
            return 0.0;
        }

        if ($coupon->discount_type === 'percent') {
            return max(0, round($amount * ((float) $coupon->discount_value) / 100, 2));
        }

        return max(0, min($amount, round((float) $coupon->discount_value, 2)));
    }

    public function redeem(
        SubscriptionCoupon $coupon,
        Company $company,
        ?int $paymentId = null,
        ?int $requestId = null
    ): void {
        SubscriptionCouponRedemption::create([
            'subscription_coupon_id' => $coupon->id,
            'company_id' => $company->id,
            'subscription_payment_id' => $paymentId,
            'subscription_request_id' => $requestId,
        ]);

        $coupon->increment('used_count');
    }
}
