<?php

namespace App\Livewire\Admin;

use App\Models\Customer;
use App\Models\PlanFamily;
use App\Services\Billing\PlanCatalogue;
use App\Services\Billing\WithdrawalRight;
use Illuminate\Support\Number;
use Livewire\Attributes\Layout;
use Livewire\Component;

#[Layout('layouts.admin')]
class Customers extends Component
{
    /** Suspend / reactivate a customer account (a guarded lifecycle action, not delete). */
    public function toggleSuspend(string $uuid): void
    {
        $this->authorize('customers.manage');
        $customer = Customer::query()->where('uuid', $uuid)->first();
        if ($customer === null) {
            return;
        }

        // A closed account is terminal — the suspend/reactivate toggle must not
        // resurrect it to active while closed_at is still set.
        if ($customer->closed_at !== null || $customer->status === 'closed') {
            return;
        }

        $customer->update([
            'status' => $customer->status === 'suspended' ? 'active' : 'suspended',
        ]);

        $this->dispatch('notify', message: __('admin.customer_'.($customer->status === 'suspended' ? 'suspended' : 'reactivated')));
    }

    public function render()
    {
        $locale = app()->getLocale();
        $plans = app(PlanCatalogue::class)->sellable();

        $customers = Customer::query()
            ->with(['instances.subscription'])
            ->orderBy('name')
            ->get();

        $rows = $customers->map(function (Customer $c) use ($plans, $locale) {
            $instance = $c->instances->sortByDesc('id')->first();
            $planKey = $instance->plan ?? null;
            // What this customer actually pays, off their contract — not what
            // the plan costs today. Otherwise a price rise inflates reported
            // revenue for every grandfathered customer overnight.
            //
            // Per MONTH, whatever the term: a yearly contract stores the whole
            // year, and adding that to a monthly column would report twelve
            // times the revenue for anyone who paid up front.
            $contract = $instance?->subscription;

            // The customer is already loaded on this row, so hand it to the
            // contract rather than letting WithdrawalRight fetch it again —
            // that is one query per customer on a page that lists all of them.
            $contract?->setRelation('customer', $c);

            $priceCents = (int) ($contract?->monthlyPriceCents()
                ?? ($planKey !== null ? ($plans[$planKey]['price_cents'] ?? 0) : 0));

            return [
                'uuid' => $c->uuid,
                'name' => $c->name,
                'plan' => $planKey !== null ? __('billing.plan.'.$planKey) : '—',
                // A gift or a discount, not a sale — flagged here rather than
                // inferred from the price, which a genuinely cheap plan could
                // also show.
                'granted' => (bool) $contract?->isGranted(),
                // What this package is about to become. An operator answering a
                // question about a customer's plan has to be able to see that
                // it is booked to shrink at the end of the term, or they will
                // quote today's package for a bill that is already scheduled to
                // change. The MRR beside it is still today's, which is correct:
                // nothing has moved yet.
                'pending_change' => $contract?->hasPendingPlanChange()
                    ? [
                        'plan' => __('billing.plan.'.$contract->pending_plan),
                        'at' => $contract->pending_effective_at->local()->isoFormat('LL'),
                    ]
                    : null,
                'mrr' => Number::currency($priceCents / 100, in: 'EUR', locale: $locale),
                'instance' => $instance->subdomain ?? '—',
                // Only an instance that exists can hand out an admin login.
                'instance_uuid' => ($instance?->status === 'active') ? $instance->uuid : null,
                // Whether a withdrawal taken by telephone or post could still be
                // recorded for this customer. Only a hint for the button: the
                // refusal that counts is made again inside WithdrawContract, so
                // a stale list cannot let one through.
                'withdrawal_open' => WithdrawalRight::for($contract)->open,
                'closed' => $c->closed_at !== null || $c->status === 'closed',
                'suspended' => $c->status === 'suspended',
                'status' => match (true) {
                    $c->closed_at !== null || $c->status === 'closed' => 'closed',
                    $c->status === 'suspended' => 'suspended',
                    default => $instance->status ?? $c->status ?? 'provisioning',
                },
            ];
        })->all();

        // Plan distribution for the doughnut — derived from live instances, and
        // keyed by what customers are actually ON, not by what is on sale. A
        // withdrawn plan still has customers, and dropping them from the chart
        // would quietly understate the estate.
        $labels = [];
        $counts = [];
        foreach (PlanFamily::query()->orderBy('tier')->pluck('key') as $planKey) {
            $n = $customers->filter(fn (Customer $c) => $c->instances->contains('plan', $planKey))->count();
            if ($n > 0) {
                $labels[] = __('billing.plan.'.$planKey);
                $counts[] = $n;
            }
        }

        return view('livewire.admin.customers', [
            'rows' => $rows,
            'plansChart' => [
                'type' => 'doughnut',
                'data' => [
                    'labels' => $labels,
                    'datasets' => [[
                        'data' => $counts,
                        'backgroundColor' => ['token:info', 'token:accent', 'token:success-bright', 'token:warning'],
                        'borderWidth' => 0,
                    ]],
                ],
                'options' => [
                    'cutout' => '62%',
                    'plugins' => ['legend' => ['position' => 'bottom', 'labels' => ['boxWidth' => 10, 'padding' => 12]]],
                ],
            ],
        ]);
    }
}
