<?php

namespace App\Services;

use App\Models\Branch;
use App\Models\Agent;
use App\Models\AgentCommission;
use App\Models\Invoice;
use App\Models\Subscription;
use App\Models\Tenant;
use App\Models\TenantPayment;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use RuntimeException;

class SubscriptionService
{
    public function __construct(
        private readonly AccountingService $accounting,
        private readonly PaymentGatewayService $gateway,
        private readonly TenantLifecycleNotificationService $notifications,
    ) {
    }

    public function approveTenant(Tenant $tenant, int $trialDays = 14): void
    {
        DB::transaction(function () use ($tenant, $trialDays): void {
            $trialEndsAt = now()->addDays($trialDays);

            $tenant->update([
                'status' => 'active',
                'approved_at' => now(),
                'rejected_at' => null,
                'rejected_reason' => null,
                'trial_ends_at' => $trialEndsAt,
                'subscription_ends_at' => $trialEndsAt,
            ]);

            Branch::withoutGlobalScopes()->firstOrCreate(
                ['tenant_id' => $tenant->id, 'code' => 'HQ'],
                ['name' => 'Head Office', 'address' => $tenant->address, 'is_active' => true],
            );

            $this->accounting->ensureChartOfAccounts($tenant);

            if ($tenant->subscription_plan_id) {
                Subscription::withoutGlobalScopes()->create([
                    'tenant_id' => $tenant->id,
                    'subscription_plan_id' => $tenant->subscription_plan_id,
                    'status' => 'trial',
                    'billing_cycle' => 'monthly',
                    'amount' => 0,
                    'starts_at' => now(),
                    'ends_at' => $trialEndsAt,
                    'trial_ends_at' => $trialEndsAt,
                    'reference' => NumberGenerator::reference('TRIAL'),
                ]);
            }
        });

        $this->notifications->tenantRegistered($tenant->refresh(), $trialDays);
    }

    public function approvePayment(TenantPayment $payment): Invoice
    {
        $invoice = DB::transaction(function () use ($payment): Invoice {
            $tenant = $payment->tenant()->withoutGlobalScopes()->firstOrFail();
            $plan = $payment->plan;
            $start = Carbon::parse($tenant->subscription_ends_at)->isFuture()
                ? Carbon::parse($tenant->subscription_ends_at)
                : now();
            $end = $payment->billing_cycle === 'yearly'
                ? $start->copy()->addYear()
                : $start->copy()->addMonth();

            $payment->update([
                'status' => 'approved',
                'approved_by' => auth()->id(),
                'approved_at' => now(),
                'processed_at' => now(),
            ]);

            $tenantUpdates = [
                'subscription_plan_id' => $plan->id,
                'status' => 'active',
                'subscription_ends_at' => $end,
            ];

            if ($payment->is_auto_billed) {
                $tenantUpdates['auto_billing_last_charged_at'] = now();
                $tenantUpdates['auto_billing_next_charge_at'] = $end;
                $tenantUpdates['auto_billing_failed_at'] = null;
                $tenantUpdates['auto_billing_failure_reason'] = null;
            }

            $tenant->update($tenantUpdates);

            Subscription::withoutGlobalScopes()->create([
                'tenant_id' => $tenant->id,
                'subscription_plan_id' => $plan->id,
                'status' => 'active',
                'billing_cycle' => $payment->billing_cycle,
                'amount' => $payment->amount,
                'starts_at' => $start,
                'ends_at' => $end,
                'reference' => $payment->reference,
            ]);

            $invoice = Invoice::withoutGlobalScopes()->create([
                'tenant_id' => $tenant->id,
                'tenant_payment_id' => $payment->id,
                'invoice_number' => NumberGenerator::reference('INV'),
                'status' => 'paid',
                'amount' => $payment->amount,
                'issued_at' => now(),
                'due_at' => now(),
                'paid_at' => now(),
                'line_items' => [
                    ['description' => $plan->name.' subscription', 'amount' => (float) $payment->amount],
                ],
            ]);

            $this->recordAgentCommission($tenant->fresh('agent'), $payment, $invoice);

            return $invoice;
        });

        $this->notifications->paymentActivated($payment->fresh(), $invoice);

        return $invoice;
    }

    public function createAutoRenewalPayment(Tenant $tenant): ?Invoice
    {
        $plan = $tenant->plan;

        if (! $plan || ! $tenant->auto_billing_enabled || $tenant->auto_billing_status !== 'active') {
            return null;
        }

        $cycle = $tenant->auto_billing_cycle === 'yearly' ? 'yearly' : 'monthly';
        $amount = $cycle === 'yearly' ? $plan->price_yearly : $plan->price_monthly;

        $payment = TenantPayment::withoutGlobalScopes()->create([
            'tenant_id' => $tenant->id,
            'subscription_plan_id' => $plan->id,
            'amount' => $amount,
            'billing_cycle' => $cycle,
            'payment_channel' => 'auto_debit',
            'is_auto_billed' => true,
            'status' => 'pending',
            'reference' => NumberGenerator::reference('AUTO'),
            'gateway_reference' => $tenant->auto_billing_reference,
            'notes' => 'Automatic '.$cycle.' subscription renewal.',
        ]);

        $charge = $this->gateway->chargeAutoRenewal($tenant, $payment);

        if (! $charge['successful']) {
            $payment->update([
                'status' => 'rejected',
                'processed_at' => now(),
                'rejected_reason' => $charge['message'],
            ]);

            $tenant->update([
                'auto_billing_status' => 'failed',
                'auto_billing_failed_at' => now(),
                'auto_billing_failure_reason' => $charge['message'],
            ]);

            throw new RuntimeException($charge['message']);
        }

        if ($charge['reference']) {
            $payment->update(['gateway_reference' => $charge['reference']]);
        }

        return $this->approvePayment($payment);
    }

    private function recordAgentCommission(Tenant $tenant, TenantPayment $payment, Invoice $invoice): void
    {
        $agent = $tenant->agent;

        if (! $agent || $agent->status !== Agent::STATUS_ACTIVE || (float) $payment->amount <= 0) {
            return;
        }

        $commissionType = $tenant->agent_commission_type ?: Agent::MERCHANT_ONBOARDING;
        $rate = $agent->commissionRateFor($commissionType);
        $amount = round(((float) $payment->amount * $rate) / 100, 2);

        AgentCommission::query()->firstOrCreate(
            ['tenant_payment_id' => $payment->id],
            [
                'agent_id' => $agent->id,
                'tenant_id' => $tenant->id,
                'invoice_id' => $invoice->id,
                'subscription_plan_id' => $payment->subscription_plan_id,
                'commissionable_amount' => $payment->amount,
                'commission_rate' => $rate,
                'commission_type' => $commissionType,
                'commission_amount' => $amount,
                'billing_cycle' => $payment->billing_cycle,
                'status' => AgentCommission::STATUS_PENDING,
                'earned_at' => now(),
                'notes' => Agent::commissionTypeLabel($commissionType).' commission from '.$tenant->name.' '.$payment->billing_cycle.' subscription payment.',
            ],
        );
    }
}
