<?php

namespace App\Domain\Subscription;

use App\Models\Plan;
use App\Models\Subscription;
use App\Models\SubscriptionPayment;
use App\Models\Tenant;
use Carbon\CarbonImmutable;
use App\Models\ActivityLog;
use Illuminate\Support\Facades\DB;

/**
 * Owns the subscription lifecycle:
 *   trialing → active → grace → suspended (→ active on payment)
 * and keeps tenants.status in sync with subscriptions.status.
 *
 * MRR is normalized to a monthly IQD figure per active/trialing tenant so
 * the financial dashboard can sum a single column.
 */
class SubscriptionService
{
    /** Extend (or start) a paid period after an approved payment. */
    public function activateFromPayment(SubscriptionPayment $payment): Subscription
    {
        return DB::transaction(function () use ($payment) {
            $tenant = $payment->tenant;
            $sub = $tenant->subscription;
            $plan = $payment->plan;
            $months = max(1, $payment->period_months);

            // New period starts at the later of now or the current period end
            // (so paying early stacks rather than wastes days).
            $base = $sub->current_period_ends_at && $sub->current_period_ends_at->isFuture()
                ? CarbonImmutable::parse($sub->current_period_ends_at)
                : CarbonImmutable::now();

            $newEnd = $base->addMonths($months);

            $sub->update([
                'plan_id' => $plan->id,
                'status' => 'active',
                'current_period_ends_at' => $newEnd,
                'grace_ends_at' => null,
                'gateway' => $payment->gateway->value,
                'expiry_notified' => null, // reset notification ladder
                'mrr_iqd' => $this->monthlyIqd($plan),
            ]);

            // Trial → paid conversion event (first paid activation from a trial).
            $wasTrial = in_array($sub->getOriginal('status'), ['trialing', 'grace'], true)
                && ! $tenant->subscription_converted_at;
            $tenant->update(['status' => 'active']);
            if ($wasTrial) {
                $tenant->forceFill(['subscription_converted_at' => now()])->saveQuietly();
                app(ActivityLogger::class)->log(
                    'trial.converted',
                    __('Trial converted to a paid subscription'),
                    $sub, $tenant->id, null,
                    ['gateway' => $payment->gateway->value, 'months' => $months],
                );
            }
            app(ActivityLogger::class)->log(
                'subscription.activated',
                __('Subscription activated until :date', ['date' => $newEnd->toDateString()]),
                $sub, $tenant->id, null,
                ['gateway' => $payment->gateway->value, 'months' => $months],
            );

            $payment->update([
                'subscription_id' => $sub->id,
                'covers_from' => $base->toDateString(),
                'covers_until' => $newEnd->toDateString(),
            ]);

            return $sub->fresh();
        });
    }

    /** Daily lifecycle sweep: trial/active → grace → suspended. */
    public function runLifecycle(): array
    {
        $now = CarbonImmutable::now();
        $graceDays = (int) config('tenancy.grace_days');
        $entered = ['grace' => 0, 'suspended' => 0];

        // Active/trialing whose period just ended → grace
        Subscription::query()->withoutGlobalScope('tenant')
            ->whereIn('status', ['trialing', 'active'])
            ->where('current_period_ends_at', '<', $now)
            ->with('tenant')->get()
            ->each(function (Subscription $sub) use ($now, $graceDays, &$entered) {
                $sub->update([
                    'status' => 'grace',
                    'grace_ends_at' => $now->addDays($graceDays),
                ]);
                $sub->tenant?->update(['status' => 'grace']);
                app(ActivityLogger::class)->log('subscription.grace',
                    __('Subscription entered grace period'), $sub, $sub->tenant_id);
                $entered['grace']++;
            });

        // Grace expired → suspended
        Subscription::query()->withoutGlobalScope('tenant')
            ->where('status', 'grace')
            ->where('grace_ends_at', '<', $now)
            ->with('tenant')->get()
            ->each(function (Subscription $sub) use (&$entered) {
                $sub->update(['status' => 'suspended', 'mrr_iqd' => 0]);
                $sub->tenant?->update(['status' => 'suspended']);
                app(ActivityLogger::class)->log('trial.expired',
                    __('Access suspended after grace period'), $sub, $sub->tenant_id);
                $entered['suspended']++;
            });

        return $entered;
    }

    public function cancel(Subscription $sub): void
    {
        $sub->update(['status' => 'cancelled', 'cancelled_at' => now(), 'mrr_iqd' => 0]);
        $sub->tenant?->update(['status' => 'cancelled']);
        app(ActivityLogger::class)->log('subscription.cancelled',
            __('Subscription cancelled'), $sub, $sub->tenant_id);
    }

    /** Normalize any plan price to a monthly IQD figure for MRR. */
    public function monthlyIqd(Plan $plan): int
    {
        return (int) $plan->price_monthly_iqd;
    }

    /** Backfill mrr_iqd for trialing/active subs (e.g. after price edit). */
    public function recalcAllMrr(): void
    {
        Subscription::query()->withoutGlobalScope('tenant')
            ->whereIn('status', ['trialing', 'active'])
            ->with('plan')->get()
            ->each(fn (Subscription $s) => $s->update(['mrr_iqd' => $this->monthlyIqd($s->plan)]));
    }
}
