<?php

declare(strict_types=1);

namespace common\services;

use common\models\Customer;
use common\models\CustomerPlan;
use common\models\Invoice;
use common\models\InvoiceItem;
use common\models\Plan;
use yii\db\Exception;

/**
 * CustomerPlanService — handles plan assignment, cancellation, expiry checks,
 * and optional auto-invoice generation.
 */
class CustomerPlanService
{
    private InvoiceService $invoiceService;

    public function __construct()
    {
        $this->invoiceService = new InvoiceService();
    }

    /**
     * Assign a plan to a customer.
     *
     * @param int    $customerId
     * @param int    $planId
     * @param string $startDate    Y-m-d
     * @param bool   $generateInvoice  Auto-create invoice for this plan?
     * @return CustomerPlan
     * @throws Exception|\Throwable
     */
    public function assignPlan(
        int    $customerId,
        int    $planId,
        string $startDate,
        bool   $generateInvoice = false
    ): CustomerPlan {
        $plan     = Plan::findOne($planId);
        $customer = Customer::findOne($customerId);

        if ($plan === null) {
            throw new \InvalidArgumentException("Plan #{$planId} not found.");
        }
        if ($customer === null) {
            throw new \InvalidArgumentException("Customer #{$customerId} not found.");
        }

        $transaction = \Yii::$app->db->beginTransaction();
        try {
            // Cancel any existing active plan for the same customer
            CustomerPlan::updateAll(
                ['status' => CustomerPlan::STATUS_CANCELLED],
                ['customer_id' => $customerId, 'status' => CustomerPlan::STATUS_ACTIVE]
            );

            $cp              = new CustomerPlan();
            $cp->customer_id = $customerId;
            $cp->plan_id     = $planId;
            $cp->start_date  = $startDate;
            $cp->status      = CustomerPlan::STATUS_ACTIVE;

            // Calculate end date for subscriptions
            if ($plan->isSubscription() && $plan->duration_months) {
                $cp->end_date = date(
                    'Y-m-d',
                    strtotime("+{$plan->duration_months} months", strtotime($startDate))
                );
            } else {
                $cp->end_date = null;
            }

            if (!$cp->save()) {
                throw new Exception('CustomerPlan save failed: ' . json_encode($cp->errors));
            }

            // Optionally generate an invoice
            if ($generateInvoice) {
                $this->invoiceService->createInvoice(
                    [
                        'customer_id'      => $customerId,
                        'customer_plan_id' => $cp->id,
                        'invoice_date'     => $startDate,
                        'due_date'         => $startDate,
                        'gst_rate'         => 18.0,
                        'discount_amount'  => 0,
                        'notes'            => "Plan: {$plan->name}",
                    ],
                    [
                        [
                            'description' => $plan->name . ($plan->duration_months ? " ({$plan->duration_months} months)" : ''),
                            'quantity'    => 1,
                            'unit_price'  => $plan->price,
                        ],
                    ]
                );
            }

            $transaction->commit();
            \Yii::$app->cache->delete('dashboard_stats');
            return $cp;
        } catch (\Throwable $e) {
            $transaction->rollBack();
            throw $e;
        }
    }

    /**
     * Cancel a customer plan.
     */
    public function cancelPlan(CustomerPlan $cp): void
    {
        $cp->status = CustomerPlan::STATUS_CANCELLED;
        $cp->save(false, ['status', 'updated_at']);
    }

    /**
     * Bulk-expire all plans whose end_date has passed.
     * Call from console command.
     *
     * @return int Number of plans expired
     */
    public function expireOverduePlans(): int
    {
        return CustomerPlan::updateAll(
            ['status' => CustomerPlan::STATUS_EXPIRED],
            [
                'AND',
                ['status' => CustomerPlan::STATUS_ACTIVE],
                ['IS NOT', 'end_date', null],
                ['<=', 'end_date', date('Y-m-d')],
            ]
        );
    }

    /**
     * Get plans expiring within $days days (for dashboard alerts).
     *
     * @return CustomerPlan[]
     */
    public function getExpiringPlans(int $days = 7): array
    {
        return CustomerPlan::find()
            ->with(['customer', 'plan'])
            ->where(['status' => CustomerPlan::STATUS_ACTIVE])
            ->andWhere(['IS NOT', 'end_date', null])
            ->andWhere(['BETWEEN', 'end_date', date('Y-m-d'), date('Y-m-d', strtotime("+{$days} days"))])
            ->orderBy('end_date')
            ->all();
    }
}
