<?php

namespace App\Livewire\HR;

use App\Domain\HR\Enums\AdvanceStatus;
use App\Domain\HR\Models\Trainer;
use App\Domain\HR\Models\TrainerAdvance;
use App\Domain\HR\Services\TrainerAdvanceService;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

#[Layout('layouts.app')]
#[Title('السلف')]
class TrainerAdvances extends Component
{
    use WithPagination, UsesBranchScope;

    #[Url]
    public string $statusFilter = '';

    #[Url]
    public string $search = '';

    // Create form
    public bool $showCreateModal = false;
    public ?int $selectedTrainerId = null;
    public string $amount = '';
    public int $installmentsCount = 3;
    public string $reason = '';

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

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

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

    public function openCreateModal(): void
    {
        $this->reset(['selectedTrainerId', 'amount', 'installmentsCount', 'reason']);
        $this->installmentsCount = 3;
        $this->showCreateModal = true;
    }

    public function createAdvance(): void
    {
        $this->validate([
            // An advance is a real money movement. exists:trainers,id is a raw
            // query that no global scope reaches, so it would take a trainer id
            // from any branch even though the dropdown is scoped.
            'selectedTrainerId' => ['required', $this->trainerExistsRule()],
            'amount' => 'required|numeric|min:1',
            'installmentsCount' => 'required|integer|min:1|max:24',
        ], [
            'selectedTrainerId.required' => 'يجب اختيار مدرب',
            'selectedTrainerId.exists' => 'المدرب المختار غير موجود في هذا الفرع',
            'amount.required' => 'يجب إدخال المبلغ',
            'amount.min' => 'المبلغ يجب أن يكون أكبر من صفر',
            'installmentsCount.required' => 'يجب تحديد عدد الأقساط',
            'installmentsCount.min' => 'قسط واحد على الأقل',
        ]);

        try {
            // findOrFail is branch-scoped — Trainer carries BranchScope — so an
            // id from another branch 404s here rather than getting an advance.
            $trainer = Trainer::findOrFail($this->selectedTrainerId);
            $amountPiasters = (int) round((float) $this->amount * 100);

            $service = app(TrainerAdvanceService::class);
            $service->create(
                $trainer,
                $amountPiasters,
                $this->installmentsCount,
                $this->reason ?: null,
                auth()->user()
            );

            $this->showCreateModal = false;
            session()->flash('success', __('تم تسجيل السلفة بنجاح'));
        } catch (\Throwable $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    /**
     * The trainer must be one this branch can actually see.
     *
     * trainers is shared-scoped — a null branch_id is a coach who works across
     * branches and stays selectable everywhere — so the rule allows the null
     * and nothing from another branch. In all-branches mode there is no branch
     * to pin to and every trainer is legitimately visible.
     */
    private function trainerExistsRule(): \Illuminate\Validation\Rules\Exists
    {
        $rule = Rule::exists('trainers', 'id');
        $branchId = $this->getActiveBranchId();

        if ($branchId !== null) {
            $rule->where(fn ($q) => $q->where(
                fn ($w) => $w->whereNull('branch_id')->orWhere('branch_id', $branchId)
            ));
        }

        return $rule;
    }

    public function cancelAdvance(int $id): void
    {
        try {
            // Branch-scoped: TrainerAdvance carries BranchScope, so an id from
            // another branch 404s instead of being cancelled.
            $advance = TrainerAdvance::findOrFail($id);
            $service = app(TrainerAdvanceService::class);
            $service->cancel($advance, auth()->user());
            session()->flash('success', __('تم إلغاء السلفة'));
        } catch (\Throwable $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function pauseAdvance(int $id): void
    {
        try {
            $advance = TrainerAdvance::findOrFail($id);
            $service = app(TrainerAdvanceService::class);
            $service->pause($advance);
            session()->flash('success', __('تم إيقاف السلفة'));
        } catch (\Throwable $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function resumeAdvance(int $id): void
    {
        try {
            $advance = TrainerAdvance::findOrFail($id);
            $service = app(TrainerAdvanceService::class);
            $service->resume($advance);
            session()->flash('success', __('تم استئناف السلفة'));
        } catch (\Throwable $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        // No branch clause: TrainerAdvance carries BranchScope and the migration
        // backfilled every row. The whereHas('trainer.employee') this used to
        // carry also required an employee record, hiding a freelance coach's
        // advances entirely.
        $query = TrainerAdvance::with(['trainer.employee.person', 'trainer.person'])
            ->latest('issued_date');

        if ($this->statusFilter) {
            $query->where('status', $this->statusFilter);
        }

        if ($this->search) {
            $search = $this->search;
            // Grouped, not chained. An un-parenthesised orWhereHas here reads as
            // `(academy AND branch AND status) OR (person name)`, so the second
            // disjunct carried no academy or branch condition at all and the
            // list leaked the moment anyone typed in the search box.
            $query->where(function ($q) use ($search) {
                $q->whereHas('trainer.employee.person', function ($p) use ($search) {
                    $p->where('name_ar', 'ilike', "%{$search}%");
                })->orWhereHas('trainer.person', function ($p) use ($search) {
                    $p->where('name_ar', 'ilike', "%{$search}%");
                });
            });
        }

        // Same here: Trainer is shared-scoped, so this is the branch's coaches
        // plus the academy-wide ones, and a coach with no employee row is no
        // longer dropped from the picker.
        $trainers = Trainer::where('status', 'active')
            ->with('employee.person', 'person')
            ->get();

        return view('livewire.hr.trainer-advances', [
            'advances' => $query->paginate(15),
            'statuses' => AdvanceStatus::cases(),
            'trainers' => $trainers,
        ]);
    }
}
