<?php

namespace App\Livewire\Admin;

use App\Models\PlanFamily;
use App\Services\Billing\PlanCatalogue;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;

/**
 * The plan lines we sell, and whether each is currently on sale.
 *
 * Deliberately shallow: a family is a name and a rank, and everything that can
 * be got wrong — capabilities, prices, windows — lives one level down on the
 * versions, where publishing makes it permanent.
 */
#[Layout('layouts.admin')]
class Plans extends Component
{
    #[Validate(['required', 'string', 'max:32', 'regex:/^[a-z][a-z0-9_]*$/', 'unique:plan_families,key'])]
    public string $key = '';

    #[Validate('required|string|max:255')]
    public string $name = '';

    #[Validate('required|integer|min:0|max:255')]
    public int $tier = 1;

    public function mount(): void
    {
        // The page itself, not only its buttons. Hiding the nav entry and
        // guarding the mutations still leaves the whole catalogue — prices,
        // drafts, unreleased plans — readable to any operator who types the URL.
        $this->authorize('plans.manage');
    }

    public function save(): void
    {
        $this->authorize('plans.manage');

        $this->key = strtolower(trim($this->key));

        $data = $this->validate([
            // The key is permanent once created — orders, instances and every
            // contract snapshot store it as a string — so it has to be a plain
            // identifier, not something anyone will want to prettify later.
            'key' => ['required', 'string', 'max:32', 'regex:/^[a-z][a-z0-9_]*$/', 'unique:plan_families,key'],
            'name' => 'required|string|max:255',
            'tier' => 'required|integer|min:0|max:255',
        ]);

        PlanFamily::create([
            'key' => $data['key'],
            'name' => $data['name'],
            'tier' => $data['tier'],
            // Nothing to sell until a version is published, so it starts able
            // to sell and simply has nothing available.
            'sales_enabled' => true,
        ]);

        $this->reset('key', 'name', 'tier');
        $this->tier = 1;
        $this->dispatch('notify', message: __('plans.created'));
    }

    /**
     * The kill switch: stop selling this plan now, without touching a window.
     *
     * Existing customers keep their contract and their machine — this only
     * decides whether anyone new can buy it.
     */
    public function toggleSales(string $uuid): void
    {
        $this->authorize('plans.manage');

        $family = PlanFamily::query()->where('uuid', $uuid)->first();
        $family?->update(['sales_enabled' => ! $family->sales_enabled]);

        $this->dispatch('notify', message: __($family?->sales_enabled ? 'plans.now_selling' : 'plans.withdrawn'));
    }

    /**
     * The second switch, per family: off the price sheet without making it
     * unsellable.
     *
     * Unabhängig von toggleSales() — `internal` und `sales_enabled` sind zwei
     * verschiedene Fragen (siehe PlanFamily::isSellableAt() und
     * PlanCatalogue::sellable()/grantable()), und dieser Schalter rührt nur an
     * der ersten. Ohne Bestätigungsmodal, wie toggleSales(): reversibel, und
     * nichts, was einem Kunden je etwas wegnimmt — ein internes Paket stand
     * ohnehin nie im Preisblatt.
     */
    public function toggleInternal(string $uuid): void
    {
        $this->authorize('plans.manage');

        $family = PlanFamily::query()->where('uuid', $uuid)->first();
        $family?->update(['internal' => ! $family->internal]);

        $this->dispatch('notify', message: __($family?->internal ? 'plans.made_internal' : 'plans.made_public'));
    }

    public function render()
    {
        $now = now();

        $families = PlanFamily::query()
            ->with(['versions.prices'])
            ->withCount('versions')
            ->orderBy('tier')
            ->get()
            ->map(fn (PlanFamily $family) => [
                'uuid' => $family->uuid,
                'key' => $family->key,
                'name' => $family->name,
                'tier' => $family->tier,
                'sales_enabled' => $family->sales_enabled,
                'is_recommended' => $family->is_recommended,
                // Ob die Familie überhaupt ein Angebot ist, unabhängig vom
                // Verkaufsschalter — der bleibt an, damit currentVersion()
                // sie fürs Verschenken weiterhin liefert.
                'internal' => $family->internal,
                'versions_count' => $family->versions_count,
                'live' => $family->versions->first(fn ($v) => $v->isAvailableAt($now)),
                // What is queued up next, so a scheduled launch is visible
                // before a customer discovers it.
                'next' => $family->versions
                    ->filter(fn ($v) => $v->isPublished() && $v->available_from->greaterThan($now))
                    ->sortBy('available_from')
                    ->first(),
                'drafts' => $family->versions->filter(fn ($v) => ! $v->isPublished())->count(),
            ]);

        return view('livewire.admin.plans', [
            'families' => $families,
            // The shop's own answer, so this page cannot disagree with what a
            // customer actually sees.
            'sellable' => array_keys(app(PlanCatalogue::class)->sellable()),
        ]);
    }
}
