<?php

namespace App\Services;

use App\Models\Addon;
use App\Models\Company;
use App\Models\SubscriptionCoupon;
use App\Models\SubscriptionPlan;
use Carbon\Carbon;

class PlanPricingService
{
    public const TERM_DAYS = 365;

    public function __construct(
        protected CouponService $couponService
    ) {}

    /**
     * Full-year purchase/renew quote.
     *
     * @return array<string, mixed>
     */
    public function calculateYearlyTotal(
        SubscriptionPlan $plan,
        ?string $couponCode = null,
        ?Company $company = null,
        array $selectedAddons = []
    ): array {
        $subtotal = (float) $plan->base_price_yearly;
        $planDiscount = $plan->planDiscountAmount();
        $afterPlan = max(0, $subtotal - $planDiscount);

        $addonsTotal = $this->addonsTotal($selectedAddons, 1);
        $afterPlan += $addonsTotal;

        $coupon = null;
        $couponDiscount = 0.0;
        if ($couponCode) {
            $coupon = $this->couponService->validate($couponCode, $company, $plan, $afterPlan);
            $couponDiscount = $this->couponService->discountAmount($coupon, $afterPlan);
        }

        $total = max(0, round($afterPlan - $couponDiscount, 2));

        return [
            'mode' => 'purchase',
            'billing_cycle' => 'yearly',
            'subtotal_before_discount' => round($subtotal + $addonsTotal, 2),
            'plan_discount_amount' => round($planDiscount, 2),
            'addons_total' => round($addonsTotal, 2),
            'coupon_discount_amount' => round($couponDiscount, 2),
            'total' => $total,
            'coupon' => $coupon,
            'coupon_code' => $coupon?->code,
            'is_upgrade' => false,
            'days_remaining' => null,
            'unused_credit' => null,
            'upgrade_gross' => null,
            'effective_yearly' => $plan->effectiveYearlyPrice(),
            'selected_addons' => array_values(array_unique(array_map('intval', $selectedAddons))),
        ];
    }

    /**
     * Mid-cycle upgrade quote (licence end date unchanged).
     *
     * @return array<string, mixed>
     */
    public function calculateUpgradeQuote(
        Company $company,
        SubscriptionPlan $newPlan,
        ?string $couponCode = null
    ): array {
        $oldPlan = $company->subscriptionPlan;
        $daysRemaining = $this->daysRemainingInclusive($company->licence_expire_on);
        $termDays = self::TERM_DAYS;

        $oldEffective = $oldPlan ? $oldPlan->effectiveYearlyPrice() : 0.0;
        $newEffective = $newPlan->effectiveYearlyPrice();

        $unusedCredit = round($oldEffective * ($daysRemaining / $termDays), 2);
        $newProrata = round($newEffective * ($daysRemaining / $termDays), 2);
        $grossDue = max(0, round($newProrata - $unusedCredit, 2));

        $planDiscountShown = max(0, round(
            ((float) $newPlan->base_price_yearly - $newEffective) * ($daysRemaining / $termDays),
            2
        ));

        $coupon = null;
        $couponDiscount = 0.0;
        if ($couponCode) {
            $coupon = $this->couponService->validate($couponCode, $company, $newPlan, $grossDue);
            $couponDiscount = $this->couponService->discountAmount($coupon, $grossDue);
        }

        $total = max(0, round($grossDue - $couponDiscount, 2));

        return [
            'mode' => 'upgrade',
            'billing_cycle' => 'yearly',
            'subtotal_before_discount' => round(($newPlan->base_price_yearly) * ($daysRemaining / $termDays), 2),
            'plan_discount_amount' => $planDiscountShown,
            'addons_total' => 0.0,
            'coupon_discount_amount' => round($couponDiscount, 2),
            'total' => $total,
            'coupon' => $coupon,
            'coupon_code' => $coupon?->code,
            'is_upgrade' => true,
            'days_remaining' => $daysRemaining,
            'unused_credit' => $unusedCredit,
            'upgrade_gross' => $grossDue,
            'effective_yearly' => $newEffective,
            'old_effective_yearly' => $oldEffective,
            'selected_addons' => [],
        ];
    }

    /**
     * Resolve checkout quote for a company targeting a plan.
     *
     * @return array<string, mixed>
     */
    public function quoteForCheckout(
        Company $company,
        SubscriptionPlan $plan,
        string $mode,
        ?string $couponCode = null
    ): array {
        $mode = in_array($mode, ['purchase', 'upgrade', 'renew'], true) ? $mode : 'purchase';

        if ($mode === 'upgrade') {
            return $this->calculateUpgradeQuote($company, $plan, $couponCode);
        }

        $quote = $this->calculateYearlyTotal($plan, $couponCode, $company);
        $quote['mode'] = $mode;

        return $quote;
    }

    public function canUpgradeTo(Company $company, SubscriptionPlan $newPlan): bool
    {
        if ($newPlan->is_custom) {
            return false;
        }

        if (! $company->licence_expire_on || $company->isLicenceExpired()) {
            return false;
        }

        if (! $company->isAccessAllowed() && $company->status !== 'suspended') {
            return false;
        }

        $current = $company->subscriptionPlan;
        if (! $current) {
            return true;
        }

        $currentLimit = $current->employee_limit;
        $newLimit = $newPlan->employee_limit;

        if ($currentLimit === null) {
            return false;
        }

        if ($newLimit === null) {
            return true;
        }

        return (int) $newLimit > (int) $currentLimit;
    }

    public function suggestNextPlan(Company $company): ?SubscriptionPlan
    {
        $currentLimit = $company->employeeSeatLimit() ?? 0;

        return SubscriptionPlan::query()
            ->where('active', true)
            ->where('is_custom', false)
            ->where(function ($q) {
                $q->where('default', '!=', 'yes')->orWhereNull('default');
            })
            ->whereNotNull('employee_limit')
            ->where('employee_limit', '>', $currentLimit)
            ->orderBy('employee_limit')
            ->first();
    }

    /**
     * Back-compat wrapper used by older call sites.
     *
     * @return array<string, mixed>
     */
    public function calculateTotal(
        SubscriptionPlan $plan,
        int $employeeCount,
        array $selectedAddons = [],
        string $billingCycle = 'yearly'
    ): array {
        $quote = $this->calculateYearlyTotal($plan, null, null, $selectedAddons);
        $quote['employee_count'] = max(1, $employeeCount);
        $quote['billing_cycle'] = 'yearly';
        $quote['base_price'] = (float) $plan->base_price_yearly;
        $quote['employee_cost'] = 0.0;

        return $quote;
    }

    public function daysRemainingInclusive(?Carbon $licenceExpireOn): int
    {
        if (! $licenceExpireOn) {
            return 0;
        }

        $today = now()->startOfDay();
        $end = $licenceExpireOn->copy()->startOfDay();
        if ($end->lt($today)) {
            return 0;
        }

        return (int) $today->diffInDays($end) + 1;
    }

    /**
     * @param  array<int, int|string>  $selectedAddons
     */
    protected function addonsTotal(array $selectedAddons, int $employeeCount): float
    {
        if ($selectedAddons === []) {
            return 0.0;
        }

        $addons = Addon::query()
            ->whereIn('id', $selectedAddons)
            ->get(['id', 'pricing_type', 'price']);

        return (float) $addons->sum(function (Addon $addon) use ($employeeCount) {
            $price = (float) $addon->price;
            if ($addon->pricing_type === 'per_employee') {
                return $employeeCount * $price;
            }

            return $price;
        });
    }
}
