<?php

namespace App\Livewire\HR;

use App\Domain\HR\Enums\AvailabilityType;
use App\Domain\HR\Enums\CompensationModel;
use App\Domain\HR\Enums\EmploymentType;
use App\Domain\HR\Services\EmployeeService;
use App\Domain\HR\Services\TrainerService;
use App\Domain\Identity\Models\Person;
use App\Domain\Identity\Models\Role;
use App\Domain\Scheduling\Enums\AssignmentScope;
use App\Domain\Scheduling\Models\Assignment;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\Activity;
use App\Domain\Training\Models\TrainingGroup;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('إضافة مدرب جديد')]
class CreateTrainerWizard extends Component
{
    use UsesBranchScope;

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

    // Step 1: Person
    public ?int $personId = null;
    public string $personSearch = '';
    public bool $createNewPerson = false;
    public string $personNameAr = '';
    public string $personName = '';
    public string $personEmail = '';
    public string $personPhone = '';
    public string $personNationalId = '';
    public string $personDateOfBirth = '';
    public string $personGender = '';

    // Step 2: Employment
    public string $department = '';
    public string $position = '';
    public string $employmentType = '';
    public string $startDate = '';
    public ?int $branchId = null;

    // Step 3: Activities & Sports
    public array $selectedActivities = [];
    public ?int $primaryActivityId = null;

    // Step 4: User Account
    public bool $createUserAccount = false;
    public string $userEmail = '';
    public string $userPassword = '';
    public bool $autoGeneratePassword = true;
    public bool $sendCredentials = false;

    // Step 5: Compensation
    public string $compensationModel = '';
    public string $monthlySalary = '';
    public string $hourlyRate = '';
    public string $sessionRate = '';
    public string $groupRate = '';
    public string $playerRate = '';
    public string $revenueSharePercent = '';

    // Step 6: Availability
    public array $availabilities = [];

    // Step 7: Qualifications
    public array $qualifications = [];

    // Step 8: Initial Assignment
    public bool $assignToGroup = false;
    public ?int $assignToGroupId = null;
    public string $assignmentScope = 'full';

    public function mount(): void
    {
        $this->authorize('trainers.create');
        $this->startDate = now()->toDateString();
        $this->position = 'مدرب';
        $this->department = 'التدريب';
        // branchId lands on the employee record, on the trainer, and on the
        // role_user pivot. Default it to the branch the operator is in; only an
        // all-branches user is offered a choice.
        $this->branchId = $this->getActiveBranchId();

        // Initialize 7 days availability
        $days = ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'];
        foreach ($days as $index => $day) {
            $this->availabilities[$index] = [
                'day_of_week' => $index,
                'day_name' => $day,
                'enabled' => false,
                'start_time' => '08:00',
                'end_time' => '17:00',
                'type' => 'available',
            ];
        }

        // Initialize empty qualification
        $this->qualifications = [
            ['name' => '', 'issuer' => '', 'issue_date' => '', 'expiry_date' => ''],
        ];
    }

    public function getStepLabels(): array
    {
        return [
            1 => 'بيانات الشخص',
            2 => 'التوظيف',
            3 => 'الأنشطة',
            4 => 'حساب الدخول',
            5 => 'التعويض',
            6 => 'التوفر',
            7 => 'المؤهلات',
            8 => 'التعيين',
            9 => 'مراجعة',
        ];
    }

    public function getPersonResultsProperty(): \Illuminate\Support\Collection
    {
        if (strlen($this->personSearch) < 2) {
            return collect();
        }

        return Person::where(function ($q) {
            $q->where('name_ar', 'ilike', "%{$this->personSearch}%")
              ->orWhere('name', 'ilike', "%{$this->personSearch}%")
              ->orWhere('phone', 'like', "%{$this->personSearch}%")
              ->orWhere('national_id', 'like', "%{$this->personSearch}%");
        })->limit(10)->get(['id', 'name', 'name_ar', 'phone', 'email', 'national_id']);
    }

    public function getAvailableGroupsProperty(): \Illuminate\Support\Collection
    {
        if (empty($this->selectedActivities)) {
            return collect();
        }

        return TrainingGroup::whereHas('program', function ($q) {
            $q->whereIn('activity_id', $this->selectedActivities);
        })
            ->whereIn('status', ['forming', 'active'])
            ->with('program:id,name_ar,activity_id')
            ->orderBy('name_ar')
            ->get(['id', 'name_ar', 'training_program_id', 'status', 'current_count', 'max_capacity']);
    }

    public function selectPerson(int $id): void
    {
        $person = Person::findOrFail($id);
        $this->personId = $person->id;
        $this->personNameAr = $person->name_ar ?? '';
        $this->personName = $person->name ?? '';
        $this->personEmail = $person->email ?? '';
        $this->personPhone = $person->phone ?? '';
        $this->personNationalId = $person->national_id ?? '';
        $this->personDateOfBirth = $person->date_of_birth?->toDateString() ?? '';
        $this->personGender = $person->gender ?? '';
        $this->createNewPerson = false;
        $this->personSearch = '';

        // Pre-fill user email from person if available
        if ($person->email && empty($this->userEmail)) {
            $this->userEmail = $person->email;
        }
    }

    public function toggleCreateNew(): void
    {
        $this->createNewPerson = !$this->createNewPerson;
        if ($this->createNewPerson) {
            $this->personId = null;
        }
    }

    public function updatedCreateUserAccount(): void
    {
        if ($this->createUserAccount && $this->autoGeneratePassword) {
            $this->userPassword = Str::random(10);
        }
        // Pre-fill email from person data
        if ($this->createUserAccount && empty($this->userEmail)) {
            $this->userEmail = $this->personEmail;
        }
    }

    public function updatedAutoGeneratePassword(): void
    {
        if ($this->autoGeneratePassword) {
            $this->userPassword = Str::random(10);
        } else {
            $this->userPassword = '';
        }
    }

    public function toggleActivity(int $activityId): void
    {
        if (in_array($activityId, $this->selectedActivities)) {
            $this->selectedActivities = array_values(array_diff($this->selectedActivities, [$activityId]));
            // Reset primary if removed
            if ($this->primaryActivityId === $activityId) {
                $this->primaryActivityId = !empty($this->selectedActivities) ? $this->selectedActivities[0] : null;
            }
            // Reset group assignment if no activities selected
            if (empty($this->selectedActivities)) {
                $this->assignToGroup = false;
                $this->assignToGroupId = null;
            }
        } else {
            $this->selectedActivities[] = $activityId;
            // Auto-set primary to first selected
            if ($this->primaryActivityId === null) {
                $this->primaryActivityId = $activityId;
            }
        }
    }

    public function addQualification(): void
    {
        $this->qualifications[] = ['name' => '', 'issuer' => '', 'issue_date' => '', 'expiry_date' => ''];
    }

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

    public function rulesForStep(int $step): array
    {
        return match ($step) {
            1 => $this->createNewPerson ? [
                'personNameAr' => 'required|string|max:255',
                'personPhone' => 'required|string|max:20',
                'personGender' => 'required|in:male,female',
            ] : [
                'personId' => 'required|exists:people,id',
            ],
            2 => [
                'department' => 'required|string|max:100',
                'position' => 'required|string|max:100',
                'employmentType' => 'required|in:' . implode(',', array_column(EmploymentType::cases(), 'value')),
                'startDate' => 'required|date',
                // exists:branches,id would take any branch in the academy. This
                // value is written to employees.branch_id and role_user.branch_id,
                // so it has to be one the user may actually pick.
                'branchId' => ['required', Rule::in($this->selectableBranches()->pluck('id')->all())],
            ],
            3 => [
                'selectedActivities' => 'required|array|min:1',
                'selectedActivities.*' => 'exists:activities,id',
                'primaryActivityId' => 'required|in_array:selectedActivities.*',
            ],
            4 => $this->createUserAccount ? [
                'userEmail' => 'required|email|unique:users,email',
                'userPassword' => 'required|string|min:8',
            ] : [],
            5 => array_merge(
                ['compensationModel' => 'required|in:' . implode(',', array_column(CompensationModel::cases(), 'value'))],
                match ($this->compensationModel) {
                    'hourly' => ['hourlyRate' => 'required|numeric|min:1'],
                    'per_session' => ['sessionRate' => 'required|numeric|min:1'],
                    'per_group' => ['groupRate' => 'required|numeric|min:1'],
                    'per_player' => ['playerRate' => 'required|numeric|min:1'],
                    'revenue_share' => ['revenueSharePercent' => 'required|numeric|min:0.01|max:100'],
                    'hybrid' => ['sessionRate' => 'required|numeric|min:1'],
                    'contract' => ['sessionRate' => 'required|numeric|min:1'],
                    'salary' => ['monthlySalary' => 'required|numeric|min:1'],
                    default => [],
                }
            ),
            6 => [],
            7 => [],
            8 => $this->assignToGroup ? [
                // exists: is a raw query and skips TrainingGroup's BranchScope,
                // so it would happily attach the new trainer to another
                // branch's group. Validate against the very set the picker
                // offered — that query IS scoped.
                'assignToGroupId' => ['required', Rule::in($this->availableGroups->pluck('id')->all())],
                'assignmentScope' => 'required|in:' . implode(',', array_column(AssignmentScope::cases(), 'value')),
            ] : [],
            default => [],
        };
    }

    public function messagesForStep(int $step): array
    {
        return match ($step) {
            1 => [
                'personId.required' => 'يجب اختيار شخص من القائمة',
                'personId.exists' => 'الشخص المختار غير موجود',
                'personNameAr.required' => 'الاسم بالعربي مطلوب',
                'personPhone.required' => 'رقم الهاتف مطلوب',
                'personGender.required' => 'الجنس مطلوب',
                'personGender.in' => 'قيمة الجنس غير صالحة',
            ],
            2 => [
                'department.required' => 'القسم مطلوب',
                'position.required' => 'المسمى الوظيفي مطلوب',
                'employmentType.required' => 'نوع التوظيف مطلوب',
                'employmentType.in' => 'نوع التوظيف غير صالح',
                'startDate.required' => 'تاريخ البدء مطلوب',
                'branchId.required' => 'يجب اختيار الفرع',
                'branchId.in' => 'الفرع المختار غير متاح',
            ],
            3 => [
                'selectedActivities.required' => 'يجب اختيار نشاط واحد على الأقل',
                'selectedActivities.min' => 'يجب اختيار نشاط واحد على الأقل',
                'primaryActivityId.required' => 'يجب تحديد التخصص الأساسي',
                'primaryActivityId.in_array' => 'التخصص الأساسي يجب أن يكون من الأنشطة المختارة',
            ],
            4 => [
                'userEmail.required' => 'البريد الإلكتروني مطلوب لإنشاء حساب الدخول',
                'userEmail.email' => 'صيغة البريد الإلكتروني غير صحيحة',
                'userEmail.unique' => 'البريد الإلكتروني مستخدم بالفعل',
                'userPassword.required' => 'كلمة المرور مطلوبة',
                'userPassword.min' => 'كلمة المرور يجب أن تكون 8 أحرف على الأقل',
            ],
            5 => [
                'compensationModel.required' => 'نموذج التعويض مطلوب',
                'compensationModel.in' => 'نموذج التعويض غير صالح',
                'monthlySalary.required' => 'الراتب الشهري مطلوب',
                'monthlySalary.numeric' => 'الراتب الشهري يجب أن يكون رقماً',
                'monthlySalary.min' => 'الراتب الشهري يجب أن يكون أكبر من صفر',
                'hourlyRate.required' => 'معدل الساعة مطلوب لهذا النموذج',
                'hourlyRate.numeric' => 'معدل الساعة يجب أن يكون رقماً',
                'hourlyRate.min' => 'معدل الساعة يجب أن يكون أكبر من صفر',
                'sessionRate.required' => 'معدل الحصة مطلوب لهذا النموذج',
                'sessionRate.numeric' => 'معدل الحصة يجب أن يكون رقماً',
                'sessionRate.min' => 'معدل الحصة يجب أن يكون أكبر من صفر',
                'groupRate.required' => 'معدل المجموعة مطلوب لهذا النموذج',
                'groupRate.numeric' => 'معدل المجموعة يجب أن يكون رقماً',
                'groupRate.min' => 'معدل المجموعة يجب أن يكون أكبر من صفر',
                'playerRate.required' => 'معدل اللاعب مطلوب لهذا النموذج',
                'playerRate.numeric' => 'معدل اللاعب يجب أن يكون رقماً',
                'playerRate.min' => 'معدل اللاعب يجب أن يكون أكبر من صفر',
                'revenueSharePercent.required' => 'نسبة المشاركة مطلوبة لهذا النموذج',
                'revenueSharePercent.numeric' => 'نسبة المشاركة يجب أن تكون رقماً',
                'revenueSharePercent.min' => 'نسبة المشاركة يجب أن تكون أكبر من صفر',
                'revenueSharePercent.max' => 'نسبة المشاركة لا يمكن أن تتجاوز 100%',
            ],
            8 => [
                'assignToGroupId.required' => 'يجب اختيار المجموعة',
                'assignToGroupId.in' => 'المجموعة غير موجودة في هذا الفرع',
                'assignmentScope.required' => 'يجب تحديد نطاق التعيين',
                'assignmentScope.in' => 'نطاق التعيين غير صالح',
            ],
            default => [],
        };
    }

    public function nextStep(): void
    {
        $rules = $this->rulesForStep($this->currentStep);

        if (!empty($rules)) {
            $this->validate($rules, $this->messagesForStep($this->currentStep));
        }

        if ($this->currentStep < $this->totalSteps) {
            $this->currentStep++;
        }
    }

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

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

    public function confirm(): void
    {
        // currentStep is a plain public property, so the browser can set it to
        // the review step and call confirm() without nextStep() ever running —
        // which is how branchId and assignToGroupId reached the write with
        // nothing checked. Both are written straight onto records below (the
        // employee's branch, the role_user pivot, the assignment's group), so
        // the check is repeated here, where the write happens.
        foreach (range(1, $this->totalSteps) as $step) {
            $rules = $this->rulesForStep($step);

            if (! empty($rules)) {
                $this->validate($rules, $this->messagesForStep($step));
            }
        }

        try {
            DB::transaction(function () {
                $person = $this->resolveOrCreatePerson();
                $academyId = app('current_academy')->id;
                $user = null;

                // Step 4: Create User Account if requested
                if ($this->createUserAccount) {
                    $trainerRole = Role::where('academy_id', $academyId)
                        ->where('slug', 'trainer')
                        ->first();

                    $user = User::create([
                        'academy_id' => $academyId,
                        'person_id' => $person->id,
                        'name' => $this->personName ?: $this->personNameAr,
                        'name_ar' => $this->personNameAr,
                        'email' => $this->userEmail,
                        'phone' => $this->personPhone ?: null,
                        'gender' => $this->personGender ?: null,
                        'password' => Hash::make($this->userPassword),
                        'role_id' => $trainerRole?->id,
                        'status' => 'active',
                        'force_password_change' => !$this->autoGeneratePassword ? false : true,
                    ]);

                    // Link person to user
                    $person->update(['user_id' => $user->id]);

                    // Assign trainer role via pivot
                    if ($trainerRole) {
                        $user->roles()->attach($trainerRole->id, [
                            'branch_id' => $this->branchId,
                            'assigned_by' => auth()->id(),
                            'created_at' => now(),
                        ]);
                    }
                }

                // Step 2: Create Employee
                $employeeData = [
                    'academy_id' => $academyId,
                    'person_id' => $person->id,
                    'user_id' => $user?->id,
                    'department' => $this->department,
                    'position' => $this->position,
                    'employment_type' => $this->employmentType,
                    'start_date' => $this->startDate,
                    'branch_id' => $this->branchId,
                    'salary_amount' => $this->monthlySalary ? (int) round((float) $this->monthlySalary * 100) : null,
                    'salary_frequency' => $this->monthlySalary ? 'monthly' : null,
                ];

                $employee = app(EmployeeService::class)->create($employeeData, auth()->user());

                // Step 5: Create Trainer
                $trainerData = [
                    'person_id' => $person->id,
                    '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,
                    'sports' => $this->selectedActivities,
                    'specializations' => [$this->primaryActivityId],
                ];

                $trainerService = app(TrainerService::class);
                $trainer = $trainerService->create($employee, $trainerData, auth()->user());

                // Step 6: Set Availability
                $slots = collect($this->availabilities)
                    ->filter(fn ($a) => $a['enabled'])
                    ->map(fn ($a) => [
                        'day_of_week' => $a['day_of_week'],
                        'start_time' => $a['start_time'],
                        'end_time' => $a['end_time'],
                        'type' => $a['type'],
                    ])
                    ->values()
                    ->toArray();

                if (!empty($slots)) {
                    $trainerService->setAvailability($trainer, $slots);
                }

                // Step 7: Add Qualifications
                $validQualifications = collect($this->qualifications)
                    ->filter(fn ($q) => !empty($q['name']));

                foreach ($validQualifications as $qual) {
                    $trainerService->addQualification($trainer, [
                        'name' => $qual['name'],
                        'issuer' => $qual['issuer'] ?: null,
                        'issue_date' => $qual['issue_date'] ?: null,
                        'expiry_date' => $qual['expiry_date'] ?: null,
                    ]);
                }

                // Step 8: Initial Assignment
                if ($this->assignToGroup && $this->assignToGroupId && $user) {
                    // The assignment belongs where the group is, not where the
                    // creating user happens to be standing — an all-branches
                    // operator would otherwise stamp it from their own pivot
                    // branch (or from nothing at all).
                    //
                    // findOrFail, and the id read back off the record: this used
                    // to fall back to `?? $this->branchId` and still write the
                    // raw property as assignable_id, so a group BranchScope had
                    // refused was filed as an assignment in the operator's own
                    // branch pointing at another branch's group.
                    $assignedGroup = TrainingGroup::findOrFail($this->assignToGroupId);

                    Assignment::create([
                        'academy_id' => $academyId,
                        'branch_id' => $assignedGroup->branch_id,
                        'user_id' => $user->id,
                        'assignable_type' => TrainingGroup::class,
                        'assignable_id' => $assignedGroup->id,
                        'role_label' => 'مدرب',
                        'scope' => $this->assignmentScope,
                        'status' => 'active',
                        'start_date' => $this->startDate,
                        'is_primary' => true,
                        'created_by' => auth()->id(),
                    ]);
                }

                // Dispatch credential email event if requested
                if ($this->createUserAccount && $this->sendCredentials && $user) {
                    // Dispatch event — listener will handle the email
                    event(new \App\Domain\HR\Events\TrainerCredentialsGenerated($user, $this->userPassword));
                }
            });

            $this->completed = true;
            session()->flash('success', 'تم إضافة المدرب بنجاح');
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Illuminate\Database\QueryException $e) {
            if (str_contains($e->getMessage(), 'users_email_unique')) {
                session()->flash('error', 'البريد الإلكتروني مستخدم بالفعل في حساب آخر');
            } else {
                session()->flash('error', 'حدث خطأ أثناء الحفظ: ' . $e->getMessage());
            }
        }
    }

    private function resolveOrCreatePerson(): Person
    {
        if ($this->personId) {
            return Person::findOrFail($this->personId);
        }

        return Person::create([
            'academy_id' => app('current_academy')->id,
            'name_ar' => $this->personNameAr,
            'name' => $this->personName ?: null,
            'email' => $this->personEmail ?: null,
            'phone' => $this->personPhone,
            'national_id' => $this->personNationalId ?: null,
            'date_of_birth' => $this->personDateOfBirth ?: null,
            'gender' => $this->personGender,
            'created_by' => auth()->id(),
        ]);
    }

    public function render()
    {
        return view('livewire.hr.create-trainer-wizard', [
            'stepLabels' => $this->getStepLabels(),
            'branches' => $this->selectableBranches(),
            'employmentTypes' => EmploymentType::cases(),
            'compensationModels' => CompensationModel::cases(),
            'availabilityTypes' => AvailabilityType::cases(),
            'activities' => Activity::where('is_active', true)->orderBy('name_ar')->get(['id', 'name_ar', 'name', 'icon', 'color']),
            'assignmentScopes' => AssignmentScope::cases(),
        ]);
    }
}
