<?php

namespace App\Livewire\Documents;

use App\Domain\Document\DocumentSettings;
use App\Domain\Document\Enums\DocumentType;
use App\Domain\Document\Services\DocumentService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Models\SystemSetting;
use Illuminate\Database\Eloquent\Model;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;
use App\Domain\Shared\Traits\ChecksTemporaryUploads;
use Livewire\WithFileUploads;

#[Layout('layouts.app')]
#[Title('رفع مستند')]
class DocumentUploadWizard extends Component
{
    use WithFileUploads;
    use ChecksTemporaryUploads;

    public int $currentStep = 1;
    public int $totalSteps = 4;
    public bool $completed = false;

    /**
     * What the document is being filed against. Set once from the route in
     * mount() and never bound to an input, so it is server state: in Livewire
     * v4 leaving these unlocked would let the page swap the target between
     * round-trips and have confirm() file the upload against a different
     * record than the one mount() authorised and named on screen.
     *
     * Branch is not re-checked by hand below — see resolveDocumentable().
     */
    #[Locked]
    public string $documentableType = '';

    #[Locked]
    public int $documentableId = 0;

    #[Locked]
    public string $documentableLabel = '';

    public string $documentType = '';
    public $file = null;
    public string $notes = '';

    public ?array $createdDocument = null;

    public function mount(string $documentableType, int $documentableId): void
    {
        $this->authorize('documents.upload');

        $this->documentableType = $documentableType;
        $this->documentableId = $documentableId;

        $model = $this->resolveDocumentable();
        abort_unless($model, 404);

        $this->documentableLabel = $model->name_ar ?? $model->name ?? "#{$model->id}";
    }

    public function nextStep(): void
    {
        if (! $this->temporaryUploadIsUsable('file', $this->file)) {
            return;
        }

        $this->validate($this->rulesForStep($this->currentStep));
        $this->currentStep = min($this->currentStep + 1, $this->totalSteps);
    }

    public function previousStep(): void
    {
        $this->currentStep = max($this->currentStep - 1, 1);
    }

    public function goToStep(int $step): void
    {
        if ($step < $this->currentStep) {
            $this->currentStep = $step;
        }
    }

    public function confirm(DocumentService $service): void
    {
        try {
            $model = $this->resolveDocumentable();
            abort_unless($model, 404);

            $type = DocumentType::from($this->documentType);

            $document = $service->upload(
                $model,
                $this->file,
                $type,
                $this->notes ?: null,
                auth()->user()
            );

            $this->createdDocument = [
                'uuid' => $document->uuid,
                'type_label' => $type->label(),
                'status_label' => $document->status->label(),
                'original_filename' => $document->original_filename,
            ];

            $this->completed = true;
            $this->currentStep = $this->totalSteps;
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        return view('livewire.documents.document-upload-wizard', [
            'allowedTypes' => app(DocumentService::class)->getAllowedTypes(),
            'maxSizeMb' => (int) SystemSetting::get(DocumentSettings::MAX_DOCUMENT_SIZE_MB, 10),
            'stepLabels' => $this->getStepLabels(),
        ]);
    }

    private function rulesForStep(int $step): array
    {
        $maxSize = (int) SystemSetting::get(DocumentSettings::MAX_DOCUMENT_SIZE_MB, 10);

        return match ($step) {
            1 => [
                'documentType' => 'required|in:' . implode(',', array_column(DocumentType::cases(), 'value')),
            ],
            2 => [
                'file' => "required|file|max:" . ($maxSize * 1024),
            ],
            3 => [
                'notes' => 'nullable|string|max:1000',
            ],
            default => [],
        };
    }

    private function getStepLabels(): array
    {
        return [
            1 => 'نوع المستند',
            2 => 'رفع الملف',
            3 => 'ملاحظات',
            4 => 'تأكيد',
        ];
    }

    /**
     * Resolve the subject of the upload, branch included.
     *
     * Every lookup here goes through Eloquent, so the branch boundary is
     * applied by the models themselves and must not be duplicated:
     *
     *   Participant — strict BranchScope: another branch's player is simply
     *                 not found, and mount()/confirm() 404.
     *   Employee, Trainer — shared BranchScope: this branch's staff plus the
     *                 academy-wide rows (branch_id NULL). Adding a hard
     *                 branch_id predicate here would hide exactly those shared
     *                 staff records from every branch.
     *   Person — academy-level identity with no branch of its own (rule 19's
     *                 GLOBAL bucket, `people` has no branch column). The
     *                 Document created against it still gets stamped with the
     *                 uploader's branch.
     */
    private function resolveDocumentable(): ?Model
    {
        $map = [
            'participant' => \App\Domain\Participant\Models\Participant::class,
            'person' => \App\Domain\Identity\Models\Person::class,
            'employee' => \App\Domain\HR\Models\Employee::class,
            'trainer' => \App\Domain\HR\Models\Trainer::class,
        ];

        $class = $map[$this->documentableType] ?? null;
        if (!$class) {
            return null;
        }

        return $class::find($this->documentableId);
    }
}
