<?php

namespace App\Domain\Training\Services;

use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingProgram;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class TrainingProgramService
{
    public function create(array $data, User $actor, bool $skipDefaultGroup = false): TrainingProgram
    {
        return DB::transaction(function () use ($data, $actor, $skipDefaultGroup) {
            $slug = Str::limit($data['slug'] ?? Str::slug($data['name']), 90, '');
            $exists = TrainingProgram::withTrashed()->where('slug', $slug)->exists();
            if ($exists) {
                $slug = Str::limit($slug, 85, '') . '-' . Str::random(4);
            }

            $program = TrainingProgram::create(array_merge($data, [
                'slug' => $slug,
                'created_by' => $actor->id,
            ]));

            if (!$skipDefaultGroup) {
                $this->createDefaultGroup($program, $actor);
            }

            return $program;
        });
    }

    private function createDefaultGroup(TrainingProgram $program, User $actor): TrainingGroup
    {
        $programName = $program->name_ar ?: $program->name;
        $baseCode = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', $program->name ?: $program->name_ar), 0, 6)) . '01';

        $code = $baseCode;
        $attempt = 0;
        while (TrainingGroup::withTrashed()->where('academy_id', $program->academy_id)->where('code', $code)->exists()) {
            $attempt++;
            $code = $baseCode . '-' . $attempt;
        }

        return TrainingGroup::create([
            'academy_id' => $program->academy_id,
            'training_program_id' => $program->id,
            'branch_id' => $program->branch_id,
            'head_trainer_id' => $program->default_trainer_id,
            'name' => "Group {$program->name} 1",
            'name_ar' => "مجموعة {$programName} 1",
            'code' => $code,
            'max_capacity' => $program->max_participants,
            'current_count' => 0,
            'waitlist_count' => 0,
            'status' => 'active',
            'status_changed_at' => now(),
            'start_date' => $program->program_start_date ?? now()->toDateString(),
            'end_date' => $program->program_end_date,
            'created_by' => $actor->id,
        ]);
    }

    public function update(TrainingProgram $program, array $data): TrainingProgram
    {
        return DB::transaction(function () use ($program, $data) {
            if (isset($data['slug']) && $data['slug'] !== $program->slug) {
                $exists = TrainingProgram::where('slug', $data['slug'])
                    ->where('id', '!=', $program->id)
                    ->exists();
                if ($exists) {
                    throw new DomainException('هذا المعرف مستخدم بالفعل');
                }
            }
            $program->update($data);
            return $program->fresh();
        });
    }

    public function changeStatus(TrainingProgram $program, string $newStatus): TrainingProgram
    {
        $allowed = match ($program->status->value ?? $program->status) {
            'draft' => ['active'],
            'active' => ['full', 'closed', 'archived'],
            'full' => ['active', 'closed'],
            'closed' => ['archived', 'active'],
            default => [],
        };

        if (!in_array($newStatus, $allowed)) {
            throw new DomainException("لا يمكن تغيير حالة البرنامج من '{$program->status->value}' إلى '{$newStatus}'");
        }

        $program->update(['status' => $newStatus]);
        return $program->fresh();
    }

    public function delete(TrainingProgram $program): void
    {
        DB::transaction(function () use ($program) {
            $groupIds = $program->groups()->pluck('id')->toArray();

            // Nullify transferred_to_id references from other enrollments pointing to these groups
            if ($groupIds) {
                \App\Domain\Training\Models\Enrollment::whereIn('transferred_to_id', $groupIds)
                    ->update(['transferred_to_id' => null]);
            }

            foreach ($program->groups as $group) {
                $group->sessions()->forceDelete();
                $group->schedules()->delete();
                $group->enrollments()->forceDelete();
                $group->waitlists()->delete();
                \App\Domain\Scheduling\Models\Assignment::where('assignable_type', TrainingGroup::class)
                    ->where('assignable_id', $group->id)
                    ->delete();
                $group->forceDelete();
            }
            $program->enrollments()->forceDelete();
            $program->forceDelete();
        });
    }
}
