<?php

namespace App\Livewire\HR;

use App\Domain\HR\Enums\EmployeeStatus;
use App\Domain\HR\Enums\EmploymentType;
use App\Domain\HR\Enums\SalaryFrequency;
use App\Domain\HR\Models\Employee;
use App\Domain\HR\Services\EmployeeService;
use App\Domain\Identity\Models\Person;
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 EmployeeForm extends Component
{
    use UsesBranchScope;

    public ?Employee $employee = null;
    public bool $editing = false;

    // Person selection
    public string $personSearch = '';
    public ?int $personId = null;
    public string $selectedPersonName = '';

    // Form fields
    public string $department = '';
    public string $position = '';
    public string $employmentType = 'full_time';
    public string $startDate = '';
    public ?string $endDate = null;
    public ?string $salaryAmount = null;
    public string $salaryFrequency = '';
    public ?string $workingHoursPerWeek = null;
    public string $branchId = '';
    public string $managerId = '';
    public string $notes = '';

    public function mount(?Employee $employee = null): void
    {
        if ($employee && $employee->exists) {
            $this->authorize('employees.update');
            $this->employee = $employee;
            $this->editing = true;

            $this->personId = $employee->person_id;
            $this->selectedPersonName = $employee->person->name_ar ?? '';
            $this->department = $employee->department ?? '';
            $this->position = $employee->position ?? '';
            $this->employmentType = $employee->employment_type->value;
            $this->startDate = $employee->start_date->format('Y-m-d');
            $this->endDate = $employee->end_date?->format('Y-m-d');
            $this->salaryAmount = $employee->salary_amount ? (string) ($employee->salary_amount / 100) : null;
            $this->salaryFrequency = $employee->salary_frequency?->value ?? '';
            $this->workingHoursPerWeek = $employee->working_hours_per_week ? (string) $employee->working_hours_per_week : null;
            $this->branchId = (string) ($employee->branch_id ?? '');
            $this->managerId = (string) ($employee->manager_id ?? '');
            $this->notes = $employee->notes ?? '';
        } else {
            $this->authorize('employees.create');
            $this->startDate = now()->format('Y-m-d');
            // File a new employee in the branch the user is working in. The
            // picker only appears in all-branches mode, so without this default
            // the record would land with no branch at all.
            $this->branchId = (string) ($this->getActiveBranchId() ?? '');
        }
    }

    public function rules(): array
    {
        return [
            'personId' => 'required|exists:people,id',
            'department' => 'nullable|string|max:30',
            'position' => 'nullable|string|max:50',
            'employmentType' => 'required|in:' . implode(',', array_column(EmploymentType::cases(), 'value')),
            'startDate' => 'required|date',
            'endDate' => 'nullable|date|after_or_equal:startDate',
            'salaryAmount' => 'nullable|numeric|min:0',
            'salaryFrequency' => 'nullable|in:' . implode(',', array_column(SalaryFrequency::cases(), 'value')),
            'workingHoursPerWeek' => 'nullable|numeric|min:0|max:168',
            // exists: runs a raw query that no global scope reaches, so both of
            // these have to carry the branch themselves. Without it the ids are
            // settable from the browser and the dropdown beside them is only
            // decoration.
            'branchId' => ['nullable', Rule::in($this->selectableBranches()->pluck('id')->all())],
            'managerId' => ['nullable', $this->managerExistsRule()],
            'notes' => 'nullable|string|max:2000',
        ];
    }

    public function messages(): array
    {
        return [
            'personId.required' => 'يجب اختيار شخص',
            'personId.exists' => 'الشخص المختار غير موجود',
            'employmentType.required' => 'نوع التوظيف مطلوب',
            'employmentType.in' => 'نوع التوظيف غير صالح',
            'startDate.required' => 'تاريخ البداية مطلوب',
            'startDate.date' => 'تاريخ البداية غير صالح',
            'endDate.after_or_equal' => 'تاريخ الانتهاء يجب أن يكون بعد تاريخ البداية',
            'salaryAmount.numeric' => 'الراتب يجب أن يكون رقماً',
            'salaryFrequency.in' => 'دورة الراتب غير صالحة',
            'workingHoursPerWeek.numeric' => 'ساعات العمل يجب أن تكون رقماً',
            'branchId.in' => 'الفرع المختار غير متاح',
            'managerId.exists' => 'المدير المختار غير موجود في هذا الفرع',
        ];
    }

    /**
     * The manager must be someone this branch can actually see.
     *
     * employees is shared-scoped — a null branch_id is academy-wide staff, and
     * hiding those would make an academy HR manager unpickable — so the rule
     * allows the null and nothing from another branch. In all-branches mode
     * there is no branch to pin to and every employee is legitimately visible.
     */
    private function managerExistsRule(): \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 save(EmployeeService $service): void
    {
        $this->validate();

        $data = [
            'person_id' => $this->personId,
            'department' => $this->department ?: null,
            'position' => $this->position ?: null,
            'employment_type' => $this->employmentType,
            'start_date' => $this->startDate,
            'end_date' => $this->endDate ?: null,
            'salary_amount' => $this->salaryAmount ? (int) round((float) $this->salaryAmount * 100) : null,
            'salary_frequency' => $this->salaryFrequency ?: null,
            'working_hours_per_week' => $this->workingHoursPerWeek ?: null,
            'branch_id' => $this->branchId ?: null,
            'manager_id' => $this->managerId ?: null,
            'notes' => $this->notes ?: null,
        ];

        try {
            if ($this->editing) {
                $service->update($this->employee, $data, auth()->user());
                session()->flash('success', __('تم تحديث بيانات الموظف بنجاح'));
            } else {
                $data['academy_id'] = app('current_academy')->id;
                $service->create($data, auth()->user());
                session()->flash('success', __('تم إضافة الموظف بنجاح'));
            }

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

    public function selectPerson(int $id): void
    {
        $person = Person::find($id);
        if ($person) {
            $this->personId = $person->id;
            $this->selectedPersonName = $person->name_ar;
            $this->personSearch = '';
        }
    }

    public function render()
    {
        $searchResults = [];
        if (strlen($this->personSearch) >= 2) {
            $searchResults = Person::where(function ($q) {
                $q->where('name_ar', 'ilike', "%{$this->personSearch}%")
                    ->orWhere('name', 'ilike', "%{$this->personSearch}%")
                    ->orWhere('phone', 'ilike', "%{$this->personSearch}%")
                    ->orWhere('national_id', 'ilike', "%{$this->personSearch}%");
            })->limit(10)->get();
        }

        return view('livewire.hr.employee-form', [
            'searchResults' => $searchResults,
            'employmentTypes' => EmploymentType::cases(),
            'salaryFrequencies' => SalaryFrequency::cases(),
            // One entry unless the user is genuinely in all-branches mode; the
            // topbar switcher is the branch selector, not this.
            'branches' => $this->selectableBranches(),
            // No branch clause needed: Employee carries BranchScope, so this is
            // already this branch's staff plus the academy-wide ones.
            'managers' => Employee::with('person')->active()->get(),
        ]);
    }
}
