<?php

namespace App\Livewire\HR;

use App\Domain\HR\Enums\CompensationModel;
use App\Domain\HR\Models\Employee;
use App\Domain\HR\Models\Trainer;
use App\Domain\HR\Services\TrainerService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('بيانات المدرب')]
class TrainerForm extends Component
{
    use UsesBranchScope;

    public ?Trainer $trainer = null;
    public bool $editing = false;

    // Employee selection
    public string $employeeSearch = '';
    public ?int $employeeId = null;
    public string $selectedEmployeeName = '';

    // Person fields (editable when editing)
    public string $personNameAr = '';
    public string $personName = '';
    public string $personPhone = '';
    public string $personNationalId = '';

    // Employee salary fields (editable when editing)
    public ?string $salaryAmount = null;
    public string $salaryFrequency = 'monthly';

    // Form fields
    public string $bio = '';
    public string $bioAr = '';
    public array $specializations = [];
    public array $sports = [];
    public ?string $maxDailySessions = null;
    public ?string $maxWeeklyHours = null;
    public string $compensationModel = 'salary';
    public ?string $hourlyRate = null;
    public ?string $sessionRate = null;
    public ?string $groupRate = null;
    public ?string $playerRate = null;
    public ?string $revenueSharePercent = null;

    // Qualifications
    public array $qualifications = [];
    public string $newQualName = '';
    public string $newQualIssuer = '';
    public ?string $newQualIssueDate = null;
    public ?string $newQualExpiryDate = null;

    public function mount(?Trainer $trainer = null): void
    {
        if ($trainer && $trainer->exists) {
            $this->authorize('trainers.update');
            $this->trainer = $trainer->load(['employee.person', 'qualifications']);
            $this->editing = true;

            $this->employeeId = $trainer->employee_id;
            // employee is a belongsTo between two scoped models, so it can now
            // come back null; ?? alone would not have saved the ->person call.
            $this->selectedEmployeeName = $trainer->employee?->person?->name_ar ?? '';

            // Person data
            $person = $trainer->employee?->person ?? $trainer->person;
            $this->personNameAr = $person?->name_ar ?? '';
            $this->personName = $person?->name ?? '';
            $this->personPhone = $person?->phone ?? '';
            $this->personNationalId = $person?->national_id ?? '';

            // Salary data
            if ($trainer->employee) {
                $this->salaryAmount = $trainer->employee->salary_amount ? (string) ($trainer->employee->salary_amount / 100) : null;
                $this->salaryFrequency = $trainer->employee->salary_frequency?->value ?? 'monthly';
            }

            $this->bio = $trainer->bio ?? '';
            $this->bioAr = $trainer->bio_ar ?? '';
            $this->specializations = $trainer->specializations ?? [];
            $this->sports = $trainer->sports ?? [];
            $this->maxDailySessions = $trainer->max_daily_sessions ? (string) $trainer->max_daily_sessions : null;
            $this->maxWeeklyHours = $trainer->max_weekly_hours ? (string) $trainer->max_weekly_hours : null;
            $this->compensationModel = $trainer->compensation_model->value;
            $this->hourlyRate = $trainer->hourly_rate ? (string) ($trainer->hourly_rate / 100) : null;
            $this->sessionRate = $trainer->session_rate ? (string) ($trainer->session_rate / 100) : null;
            $this->groupRate = $trainer->group_rate ? (string) ($trainer->group_rate / 100) : null;
            $this->playerRate = $trainer->player_rate ? (string) ($trainer->player_rate / 100) : null;
            $this->revenueSharePercent = $trainer->revenue_share_percent ? (string) $trainer->revenue_share_percent : null;
            $this->qualifications = $trainer->qualifications->map(fn ($q) => [
                'id' => $q->id,
                'name' => $q->name,
                'issuer' => $q->issuer,
                'issue_date' => $q->issue_date?->format('Y-m-d'),
                'expiry_date' => $q->expiry_date?->format('Y-m-d'),
                'is_verified' => $q->is_verified,
            ])->toArray();
        } else {
            $this->authorize('trainers.create');
        }
    }

    public function rules(): array
    {
        $personRules = $this->editing ? [
            'personNameAr' => 'required|string|max:100',
            'personName' => 'nullable|string|max:100',
            'personPhone' => 'nullable|string|max:20',
            'personNationalId' => 'nullable|string|max:30',
            'salaryAmount' => 'nullable|numeric|min:0',
            'salaryFrequency' => 'required|in:monthly,biweekly,weekly,daily,hourly',
        ] : [];

        return array_merge($personRules, [
            // The typeahead is branch-filtered, but exists: is a raw query that
            // skips every global scope — so without this the id is settable
            // from the browser and the trainer gets attached to another
            // branch's employee.
            'employeeId' => ['required', $this->employeeExistsRule()],
            'sports' => 'required|array|min:1',
            'sports.*' => 'integer|exists:activities,id',
            'bio' => 'nullable|string|max:2000',
            'bioAr' => 'nullable|string|max:2000',
            'compensationModel' => 'required|in:' . implode(',', array_column(CompensationModel::cases(), 'value')),
            'maxDailySessions' => 'nullable|integer|min:1|max:20',
            'maxWeeklyHours' => 'nullable|numeric|min:1|max:168',
            'hourlyRate' => 'nullable|numeric|min:0',
            'sessionRate' => 'nullable|numeric|min:0',
            'groupRate' => 'nullable|numeric|min:0',
            'playerRate' => 'nullable|numeric|min:0',
            'revenueSharePercent' => 'nullable|numeric|min:0|max:100',
        ]);
    }

    public function messages(): array
    {
        return [
            'personNameAr.required' => 'الاسم بالعربية مطلوب',
            'personNameAr.max' => 'الاسم لا يمكن أن يتجاوز 100 حرف',
            'personName.max' => 'الاسم بالإنجليزية لا يمكن أن يتجاوز 100 حرف',
            'personPhone.max' => 'رقم الهاتف لا يمكن أن يتجاوز 20 حرف',
            'personNationalId.max' => 'الرقم القومي لا يمكن أن يتجاوز 30 حرف',
            'salaryAmount.numeric' => 'المرتب يجب أن يكون رقماً',
            'salaryAmount.min' => 'المرتب لا يمكن أن يكون بالسالب',
            'salaryFrequency.required' => 'دورية الراتب مطلوبة',
            'salaryFrequency.in' => 'دورية الراتب غير صالحة',
            'employeeId.required' => 'يجب اختيار موظف',
            'employeeId.exists' => 'الموظف المختار غير موجود في هذا الفرع',
            'sports.required' => 'يجب اختيار نشاط واحد على الأقل',
            'sports.min' => 'يجب اختيار نشاط واحد على الأقل',
            'compensationModel.required' => 'نموذج التعويض مطلوب',
            'compensationModel.in' => 'نموذج التعويض غير صالح',
            'maxDailySessions.integer' => 'الحد الأقصى للحصص يجب أن يكون رقماً صحيحاً',
            'maxWeeklyHours.numeric' => 'ساعات العمل الأسبوعية يجب أن تكون رقماً',
            'revenueSharePercent.max' => 'نسبة الإيرادات لا يمكن أن تتجاوز 100%',
        ];
    }

    public function save(TrainerService $service): void
    {
        $this->validate();

        $data = [
            'bio' => $this->bio ?: null,
            'bio_ar' => $this->bioAr ?: null,
            'specializations' => $this->specializations,
            'sports' => $this->sports,
            'max_daily_sessions' => $this->maxDailySessions ? (int) $this->maxDailySessions : null,
            'max_weekly_hours' => $this->maxWeeklyHours ?: null,
            'compensation_model' => $this->compensationModel,
            'hourly_rate' => $this->hourlyRate ? (int) round((float) $this->hourlyRate * 100) : null,
            'session_rate' => $this->sessionRate ? (int) round((float) $this->sessionRate * 100) : null,
            'group_rate' => $this->groupRate ? (int) round((float) $this->groupRate * 100) : null,
            'player_rate' => $this->playerRate ? (int) round((float) $this->playerRate * 100) : null,
            'revenue_share_percent' => $this->revenueSharePercent ?: null,
        ];

        try {
            if ($this->editing) {
                $service->update($this->trainer, $data, auth()->user());

                // Update person data
                $person = $this->trainer->employee?->person ?? $this->trainer->person;
                if ($person) {
                    $person->update([
                        'name_ar' => $this->personNameAr,
                        'name' => $this->personName ?: null,
                        'phone' => $this->personPhone ?: null,
                        'national_id' => $this->personNationalId ?: null,
                    ]);
                }

                // Update salary data
                if ($this->trainer->employee) {
                    $this->trainer->employee->update([
                        'salary_amount' => $this->salaryAmount ? (int) round((float) $this->salaryAmount * 100) : null,
                        'salary_frequency' => $this->salaryFrequency,
                    ]);
                }

                session()->flash('success', __('تم تحديث بيانات المدرب بنجاح'));
            } else {
                // Branch-scoped: Employee carries BranchScope, so an id from
                // another branch 404s rather than getting a trainer record.
                $employee = Employee::findOrFail($this->employeeId);
                $service->create($employee, $data, auth()->user());
                session()->flash('success', __('تم إضافة المدرب بنجاح'));
            }

            $this->redirect(route('trainers.list'), navigate: true);
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function addQualification(): void
    {
        if (! $this->newQualName) {
            return;
        }

        $this->qualifications[] = [
            'id' => null,
            'name' => $this->newQualName,
            'issuer' => $this->newQualIssuer,
            'issue_date' => $this->newQualIssueDate,
            'expiry_date' => $this->newQualExpiryDate,
            'is_verified' => false,
        ];

        $this->newQualName = '';
        $this->newQualIssuer = '';
        $this->newQualIssueDate = null;
        $this->newQualExpiryDate = null;
    }

    public function removeQualification(int $index): void
    {
        unset($this->qualifications[$index]);
        $this->qualifications = array_values($this->qualifications);
    }

    public function selectEmployee(int $id): void
    {
        // find() is branch-scoped, so a hand-crafted id from another branch
        // resolves to null and the selection simply does not happen.
        $employee = Employee::with('person')->find($id);
        if ($employee) {
            $this->employeeId = $employee->id;
            $this->selectedEmployeeName = $employee->person?->name_ar ?? '';
            $this->employeeSearch = '';
        }
    }

    /**
     * The employee must be one this branch can actually see.
     *
     * employees is shared-scoped — a null branch_id is academy-wide staff — so
     * the rule allows the null and nothing from another branch. In all-branches
     * mode there is no branch to pin to.
     */
    private function employeeExistsRule(): \Illuminate\Validation\Rules\Exists
    {
        $rule = Rule::exists('employees', 'id')->whereNull('deleted_at');
        $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 render()
    {
        $searchResults = [];
        if (strlen($this->employeeSearch) >= 2) {
            // No branch clause: Employee carries BranchScope, which already
            // gives this branch's staff plus the academy-wide ones.
            $searchResults = Employee::with('person')
                ->active()
                ->doesntHave('trainer')
                ->whereHas('person', function ($q) {
                    $q->where('name_ar', 'ilike', "%{$this->employeeSearch}%")
                        ->orWhere('name', 'ilike', "%{$this->employeeSearch}%");
                })
                ->limit(10)->get();
        }

        return view('livewire.hr.trainer-form', [
            'searchResults' => $searchResults,
            'compensationModels' => CompensationModel::cases(),
            'activities' => \App\Domain\Training\Models\Activity::where('is_active', true)->orderBy('name_ar')->get(),
        ]);
    }
}
