<?php

namespace App\Domain\HR\Services;

use App\Domain\Attendance\Models\AttendanceRecord;
use App\Domain\HR\Models\Employee;
use App\Domain\HR\Models\Trainer;
use App\Domain\HR\Models\TrainerAvailability;
use App\Domain\HR\Models\TrainerQualification;
use App\Domain\Scheduling\Models\Assignment;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingProgram;
use App\Domain\Training\Models\TrainingSession;
use App\Models\User;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Exceptions\InvalidStatusTransitionException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class TrainerService
{
    private const VALID_TRANSITIONS = [
        'active' => ['inactive', 'on_leave', 'suspended'],
        'inactive' => ['active'],
        'on_leave' => ['active', 'suspended'],
        'suspended' => ['active', 'inactive'],
    ];

    public function create(Employee $employee, array $data, User $actor): Trainer
    {
        return DB::transaction(function () use ($employee, $data) {
            $data['academy_id'] = $employee->academy_id;
            $data['employee_id'] = $employee->id;
            $data['trainer_number'] = $this->generateTrainerNumber($employee->academy_id);

            return Trainer::create($data);
        });
    }

    public function update(Trainer $trainer, array $data, User $actor): Trainer
    {
        return DB::transaction(function () use ($trainer, $data) {
            $trainer->update($data);

            return $trainer->fresh();
        });
    }

    public function changeStatus(Trainer $trainer, string $newStatus, User $actor): Trainer
    {
        $currentStatus = $trainer->status->value;
        $allowed = self::VALID_TRANSITIONS[$currentStatus] ?? [];

        if (! in_array($newStatus, $allowed)) {
            throw new InvalidStatusTransitionException(
                "لا يمكن تغيير حالة المدرب من '{$currentStatus}' إلى '{$newStatus}'"
            );
        }

        return DB::transaction(function () use ($trainer, $newStatus) {
            $trainer->update(['status' => $newStatus]);

            return $trainer->fresh();
        });
    }

    public function addQualification(Trainer $trainer, array $data): TrainerQualification
    {
        $data['trainer_id'] = $trainer->id;
        $data['created_at'] = now();

        return TrainerQualification::create($data);
    }

    public function removeQualification(TrainerQualification $qualification): void
    {
        $qualification->delete();
    }

    public function verifyQualification(TrainerQualification $qualification, User $verifier): void
    {
        $qualification->update([
            'is_verified' => true,
            'verified_by' => $verifier->id,
            'verified_at' => now(),
        ]);
    }

    public function setAvailability(Trainer $trainer, array $slots): void
    {
        DB::transaction(function () use ($trainer, $slots) {
            $trainer->availabilities()->delete();

            $now = now();
            $records = array_map(fn (array $slot) => [
                'trainer_id' => $trainer->id,
                'type' => $slot['type'],
                'day_of_week' => $slot['day_of_week'] ?? null,
                'specific_date' => $slot['specific_date'] ?? null,
                'start_time' => $slot['start_time'],
                'end_time' => $slot['end_time'],
                'reason' => $slot['reason'] ?? null,
                'created_at' => $now,
            ], $slots);

            if (! empty($records)) {
                TrainerAvailability::insert($records);
            }
        });
    }

    public function delete(Trainer $trainer, User $actor): void
    {
        DB::transaction(function () use ($trainer, $actor) {
            $trainerUserId = $trainer->employee?->user_id;

            // 1. Cancel pending compensations (keep approved/paid as history)
            $trainer->compensations()
                ->whereIn('status', ['pending', 'disputed'])
                ->update(['status' => 'cancelled']);

            // 2. Cancel active advances (write off remaining balance)
            $trainer->advances()
                ->where('status', 'active')
                ->update([
                    'status' => 'cancelled',
                    'remaining_balance' => 0,
                ]);

            // 3. Remove future attendance expectations
            if ($trainerUserId) {
                AttendanceRecord::where('subject_type', User::class)
                    ->where('subject_id', $trainerUserId)
                    ->where('status', 'expected')
                    ->whereHas('session', fn ($q) => $q->where('session_date', '>', now()->toDateString()))
                    ->delete();
            }

            // 4. Cancel active group assignments + clear trainer references
            if ($trainerUserId) {
                Assignment::where('user_id', $trainerUserId)
                    ->where('status', 'active')
                    ->update(['status' => 'cancelled']);

                TrainingGroup::where('head_trainer_id', $trainerUserId)
                    ->update(['head_trainer_id' => null]);

                TrainingProgram::where('default_trainer_id', $trainerUserId)
                    ->update(['default_trainer_id' => null]);

                TrainingSession::where('trainer_id', $trainerUserId)
                    ->where('session_date', '>', now()->toDateString())
                    ->update(['trainer_id' => null]);

                TrainingSession::where('assistant_trainer_id', $trainerUserId)
                    ->where('session_date', '>', now()->toDateString())
                    ->update(['assistant_trainer_id' => null]);
            }

            // 5. Deactivate user account
            if ($trainerUserId) {
                User::where('id', $trainerUserId)->update(['status' => 'inactive']);
            }

            // 6. Soft-delete employee (mangles unique fields like employee_number)
            if ($trainer->employee) {
                $trainer->employee->delete();
            }

            // 7. Soft-delete the person record (frees national_id for reimport)
            $person = $trainer->employee?->person ?? $trainer->person;
            if ($person) {
                $originalNid = $person->national_id;
                $person->delete();
                // Mangle national_id to free the unique constraint
                if ($originalNid) {
                    $person->national_id = $originalNid . '_deleted_' . $trainer->id;
                    $person->saveQuietly();
                }
            }

            // 8. Delete trainer record (hard delete since no soft deletes on this table)
            Log::channel('audit')->info('trainer_deleted', [
                'trainer_id' => $trainer->id,
                'trainer_number' => $trainer->trainer_number,
                'person_name' => $person?->name_ar,
                'deleted_by' => $actor->id,
                'deleted_by_name' => $actor->name,
            ]);

            $trainer->delete();
        });
    }

    private function generateTrainerNumber(int $academyId): string
    {
        $lastNumber = Trainer::withoutGlobalScopes()
            ->where('academy_id', $academyId)
            ->max('id') ?? 0;

        $seq = str_pad($lastNumber + 1, 4, '0', STR_PAD_LEFT);

        return "TRN-{$seq}";
    }
}
