<?php

namespace App\Livewire;

use App\Enums\ProofStatus;
use App\Enums\SubscriptionStatus;
use App\Enums\TenantStatus;
use App\Models\ActivityLog;
use App\Models\SaasProduct;
use App\Models\Subscription;
use App\Models\SubscriptionPayment;
use App\Models\Tenant;
use App\Support\Sql;
use Illuminate\Support\Carbon;
use Livewire\Attributes\Title;
use Livewire\Component;

/**
 * The panel's opening screen.
 *
 * Ordered by what someone actually needs on arriving: what is waiting for a
 * decision, what is about to lapse, and only then the money charts.
 */
#[Title('Dashboard')]
class Dashboard extends Component
{
    public int $months = 12;

    /** Money that has actually been verified, per month. */
    public function revenueSeries(): array
    {
        $start = now()->copy()->startOfMonth()->subMonths($this->months - 1);

        $rows = SubscriptionPayment::query()
            ->where('status', ProofStatus::Verified)
            ->where('reviewed_at', '>=', $start)
            ->selectRaw(Sql::monthKey('reviewed_at')." as ym, SUM(amount) as total")
            ->groupBy('ym')
            ->pluck('total', 'ym');

        $series = [];

        for ($i = 0; $i < $this->months; $i++) {
            $month = $start->copy()->addMonths($i);
            $key = $month->format('Y-m');

            $series[] = [
                'label' => $month->format('M'),
                'value' => round((float) ($rows[$key] ?? 0), 2),
            ];
        }

        return $series;
    }

    /** Which rails people actually pay on — informs which accounts to keep. */
    public function gatewayMix(): array
    {
        return SubscriptionPayment::query()
            ->where('status', ProofStatus::Verified)
            ->where('reviewed_at', '>=', now()->copy()->subMonths(6))
            ->selectRaw('gateway, SUM(amount) as total')
            ->groupBy('gateway')
            ->orderByDesc('total')
            ->get()
            ->map(fn ($row) => [
                'label' => \App\Enums\PaymentGateway::labelFor($row->gateway, 'Other'),
                'value' => round((float) $row->total, 2),
            ])
            ->all();
    }

    /**
     * Monthly recurring revenue, normalised.
     *
     * An annual subscription contributes a twelfth of its price so the number
     * means the same thing whichever cycle a tenant is on.
     */
    public function mrr(): float
    {
        $live = Subscription::query()
            ->whereIn('status', [SubscriptionStatus::Active->value, SubscriptionStatus::Grace->value])
            ->where('is_trial', false)
            ->get(['amount', 'billing_cycle']);

        return round($live->sum(fn (Subscription $s) => (float) $s->amount / max(1, $s->billing_cycle->months())), 2);
    }

    public function stats(): array
    {
        $monthStart = now()->copy()->startOfMonth();

        return [
            'pending' => SubscriptionPayment::where('status', ProofStatus::Pending)->count(),
            'pending_amount' => (float) SubscriptionPayment::where('status', ProofStatus::Pending)->sum('amount'),
            'active' => Subscription::where('status', SubscriptionStatus::Active)->count(),
            'trials' => Tenant::where('status', TenantStatus::Trial)->count(),
            'tenants' => Tenant::count(),
            'new_this_month' => Tenant::where('created_at', '>=', $monthStart)->count(),
            'revenue_month' => (float) SubscriptionPayment::where('status', ProofStatus::Verified)
                ->where('reviewed_at', '>=', $monthStart)
                ->sum('amount'),
            'mrr' => $this->mrr(),
        ];
    }

    /** Subscriptions inside the next fortnight — the renewal call list. */
    public function expiringSoon()
    {
        return Subscription::query()
            ->with('tenant', 'plan')
            ->whereIn('status', [SubscriptionStatus::Active->value, SubscriptionStatus::Trial->value])
            ->whereNotNull('ends_at')
            ->whereBetween('ends_at', [now(), now()->copy()->addDays(14)])
            ->orderBy('ends_at')
            ->limit(8)
            ->get();
    }

    public function queue()
    {
        return SubscriptionPayment::query()
            ->with('tenant', 'subscription.plan')
            ->where('status', ProofStatus::Pending)
            ->oldest('created_at')
            ->limit(6)
            ->get();
    }

    public function recentActivity()
    {
        return ActivityLog::query()
            ->with('tenant')
            ->latest('id')
            ->limit(10)
            ->get();
    }

    /** Per-product breakdown, so a portfolio owner can see which one earns. */
    public function productBreakdown()
    {
        return SaasProduct::query()
            ->withCount([
                'tenants',
                'tenants as active_tenants_count' => fn ($q) => $q->where('status', TenantStatus::Active),
            ])
            ->orderBy('sort_order')
            ->get();
    }

    public function render()
    {
        return view('livewire.dashboard', [
            'stats' => $this->stats(),
            'revenue' => $this->revenueSeries(),
            'gateways' => $this->gatewayMix(),
            'expiring' => $this->expiringSoon(),
            'queue' => $this->queue(),
            'activity' => $this->recentActivity(),
            'products' => $this->productBreakdown(),
            'rangeStart' => Carbon::now()->subMonths($this->months - 1)->startOfMonth(),
        ]);
    }
}
