<?php

namespace App\Livewire\Admin;

use App\Models\InvoiceSeries;
use Illuminate\Validation\Rule;
use LivewireUI\Modal\ModalComponent;

/**
 * Editing a Rechnungskreis, with the parts that cannot move held still.
 *
 * The same shape as EditDatacenter and for the same reason: some fields stop
 * being editable once something depends on them. Here the something is a
 * document in somebody's accounts.
 *
 * The prefix is part of every number already issued. Changing it from RE to AR
 * does not rename anything — it leaves a series whose documents say RE-2026-…
 * and whose next document says AR-2026-…, with no record that they belong
 * together. Free while the series is empty, which is the case that matters: a
 * prefix chosen wrongly on the day the shop opens.
 *
 * The counter may be raised but never lowered. Lowering it re-issues a number
 * that is already printed on a document somebody holds, and gapless-and-
 * ascending is a legal requirement rather than a preference.
 */
class EditInvoiceSeries extends ModalComponent
{
    public string $uuid = '';

    public string $name = '';

    public string $prefix = '';

    public int $digits = 4;

    public bool $yearlyReset = true;

    public int $nextNumber = 1;

    public bool $active = true;

    /** How many documents this series has already issued. */
    public int $issued = 0;

    /** The lowest number this counter may still be set to. */
    public int $floor = 1;

    public function mount(string $uuid): void
    {
        $this->authorize('site.manage'); // modals are reachable without the route middleware

        $series = InvoiceSeries::query()->where('uuid', $uuid)->firstOrFail();

        $this->uuid = $uuid;
        $this->name = $series->name;
        $this->prefix = $series->prefix;
        $this->digits = (int) $series->digits;
        $this->yearlyReset = (bool) $series->yearly_reset;
        $this->nextNumber = (int) $series->next_number;
        $this->active = (bool) $series->active;
        $this->issued = $series->invoices()->count();
        $this->floor = (int) $series->next_number;
    }

    public function save()
    {
        $this->authorize('site.manage');

        $series = InvoiceSeries::query()->where('uuid', $this->uuid)->firstOrFail();

        // Recomputed from the database, never read back from the properties the
        // browser returns: these two rules are what keep already-issued numbers
        // from being re-used, and a forged field must not lift either.
        $issued = $series->invoices()->count();
        $floor = (int) $series->next_number;

        $rules = [
            'name' => 'required|string|max:120',
            'digits' => 'required|integer|min:3|max:10',
            'active' => 'boolean',
            'nextNumber' => ['required', 'integer', 'min:'.$floor],
        ];

        if ($issued === 0) {
            $rules['prefix'] = [
                'required', 'string', 'max:12', 'regex:/^[A-Z0-9-]+$/',
                Rule::unique('invoice_series', 'prefix')->ignore($series->id),
            ];
            // Only while nothing has been issued: switching an established
            // series between yearly and continuous puts a gap in one of them.
            $rules['yearlyReset'] = 'boolean';
        }

        if ($issued === 0) {
            $this->prefix = strtoupper(trim($this->prefix));
        } else {
            $this->prefix = $series->prefix;
            $this->yearlyReset = (bool) $series->yearly_reset;
        }

        $data = $this->validate($rules);

        $series->update([
            'name' => $data['name'],
            'digits' => $data['digits'],
            'next_number' => $data['nextNumber'],
            'active' => $data['active'],
            ...($issued === 0 ? [
                'prefix' => $data['prefix'],
                'yearly_reset' => $data['yearlyReset'],
            ] : []),
        ]);

        $this->dispatch('notify', message: __('finance.series_saved'));

        return $this->redirectRoute('admin.finance', navigate: true);
    }

    public function render()
    {
        return view('livewire.admin.edit-invoice-series');
    }
}
