<?php

namespace App\Livewire\Requests;

use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Exceptions\InvalidStatusTransitionException;
use App\Domain\Shared\Models\ServiceRequest;
use App\Domain\Shared\Services\ServiceRequestService;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

/**
 * One queue for every member request — freezes, transfers, cancellations,
 * renewals and excuses.
 *
 * One queue because they are one interaction: member-initiated,
 * pending/approved/rejected, with notes coming back. Two tables would have
 * meant two screens, and two screens is how you get one of them built and the
 * other left as a `// TODO` — which is precisely the state this replaces.
 *
 * Approving here has a real effect: the service applies it inside the same
 * transaction, so a request cannot end up marked approved with nothing having
 * happened. That was the previous behaviour, and a member told their
 * subscription was frozen when it was not is worse than a slow queue.
 */
#[Layout('layouts.app')]
#[Title('طلبات الأعضاء')]
class ServiceRequestQueue extends Component
{
    use WithPagination;

    #[Url(as: 'status')]
    public string $status = 'pending';

    #[Url(as: 'type')]
    public string $type = '';

    #[Locked]
    public ?int $handlingId = null;

    public string $notes = '';

    public function mount(): void
    {
        $this->authorize('participants.update');
    }

    public function updatedStatus(): void
    {
        $this->resetPage();
    }

    public function updatedType(): void
    {
        $this->resetPage();
    }

    public function handle(int $requestId): void
    {
        $this->authorize('participants.update');

        $this->handlingId = $this->inScope($requestId)->id;
        $this->notes = '';
    }

    public function cancelHandling(): void
    {
        $this->handlingId = null;
    }

    public function approve(ServiceRequestService $requests): void
    {
        $this->authorize('participants.update');

        try {
            $requests->approve($this->inScope($this->handlingId), auth()->user(), $this->notes ?: null);
            session()->flash('success', __('تمت الموافقة وتنفيذ الطلب'));
        } catch (InvalidStatusTransitionException|DomainException $e) {
            // The effect failed, so the approval did not stand — the request is
            // still pending and the reason is on screen.
            $this->addError('notes', $e->getMessage());

            return;
        }

        $this->handlingId = null;
    }

    public function reject(ServiceRequestService $requests): void
    {
        $this->authorize('participants.update');

        $this->validate(
            ['notes' => ['required', 'string', 'max:1000']],
            ['notes.required' => __('اكتب سبب الرفض — سيراه العضو')],
        );

        try {
            $requests->reject($this->inScope($this->handlingId), auth()->user(), $this->notes);
            session()->flash('success', __('تم رفض الطلب'));
        } catch (InvalidStatusTransitionException $e) {
            $this->addError('notes', $e->getMessage());

            return;
        }

        $this->handlingId = null;
    }

    public function render()
    {
        $query = ServiceRequest::query()
            ->when($this->status !== '', fn ($q) => $q->where('status', $this->status))
            ->when($this->type !== '', fn ($q) => $q->where('type', $this->type))
            ->with(['participant.person', 'user', 'trainingSession.group', 'handler'])
            ->orderBy('created_at');

        return view('livewire.requests.service-request-queue', [
            'requests' => $query->paginate(20),
            'pendingCount' => ServiceRequest::where('status', 'pending')->count(),
            'handling' => $this->handlingId
                ? ServiceRequest::with(['participant.person', 'trainingSession.group'])->find($this->handlingId)
                : null,
            'types' => ServiceRequest::TYPES,
        ]);
    }

    private function inScope(?int $requestId): ServiceRequest
    {
        abort_if($requestId === null, 400);

        // The id comes from the browser; the tenant scope is a global scope but
        // this re-reads through it rather than trusting the rendered list.
        return ServiceRequest::findOrFail($requestId);
    }
}
