<?php

namespace App\Livewire;

use App\Livewire\Concerns\ResolvesCustomer;
use App\Models\Seat;
use LivewireUI\Modal\ModalComponent;

/**
 * Edit one seat, in a modal.
 *
 * Editing used to happen in the row itself. It worked and it looked wrong: the
 * row grew, the columns beside it jumped, and a table half in edit mode reads
 * as a rendering fault rather than as a form. Anything with fields of its own
 * gets a modal — see R20.
 *
 * A modal is reachable without passing the page's route middleware, so the
 * customer is resolved here rather than trusted from the caller: the uuid comes
 * from the browser and must never reach across customers.
 */
class EditSeat extends ModalComponent
{
    use ResolvesCustomer;

    public string $uuid = '';

    public string $name = '';

    public string $email = '';

    /** Owners cannot be suspended or removed; everyone can be renamed. */
    public bool $isOwner = false;

    /**
     * Whether the address may still be corrected.
     *
     * Only while the invitation is in flight. Once someone has accepted, the
     * address IS the person: editing it would hand one employee's access to
     * another with nobody told — a transfer of access wearing the clothes of a
     * rename.
     */
    public bool $addressEditable = false;

    public function mount(string $uuid): void
    {
        $seat = $this->seat($uuid);

        abort_if($seat === null, 404);

        $this->uuid = $uuid;
        $this->name = (string) $seat->name;
        $this->email = (string) $seat->email;
        $this->isOwner = $seat->role === 'owner';
        $this->addressEditable = $seat->status === 'invited';
    }

    public function save()
    {
        $customer = $this->requireCustomer();
        $seat = $this->seat($this->uuid);

        if ($customer === null || $seat === null) {
            return $this->closeModal();
        }

        $rules = ['name' => 'nullable|string|max:255'];

        // Re-read from the record, never from the hydrated property: a forged
        // addressEditable would otherwise open the address of an accepted seat.
        if ($seat->status === 'invited') {
            $rules['email'] = 'required|email|max:255';
        }

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

        $changes = ['name' => trim($data['name'] ?? '') ?: null];

        if ($seat->status === 'invited') {
            $address = trim($data['email']);

            if ($address !== $seat->email) {
                if ($customer->seats()->where('email', $address)->whereKeyNot($seat->id)->exists()) {
                    $this->addError('email', __('users.duplicate'));

                    return null;
                }

                $changes['email'] = $address;
            }
        }

        $seat->update($changes);

        $this->dispatch('notify', message: __('users.saved'));

        return $this->redirectRoute('users', navigate: true);
    }

    private function seat(string $uuid): ?Seat
    {
        return $this->customer()?->seats()->where('uuid', $uuid)->first();
    }

    public function render()
    {
        return view('livewire.edit-seat');
    }
}
