<?php

namespace App\Livewire\Enrollments;

use App\Domain\Participant\Models\Participant;
use App\Domain\Pricing\Services\PricingService;
use App\Domain\Pricing\Services\PriceResult;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Services\SettingsService;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingProgram;
use App\Domain\Training\Models\TrainingSchedule;
use App\Domain\Training\Services\EnrollmentService;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('تسجيل في مجموعة')]
class EnrollmentForm extends Component
{
    use UsesBranchScope;

    // ─── Panel 1: Participant ─────────────────────────────────
    public string $participantSearch = '';
    public ?int $participantId = null;

    // ─── Panel 2: Group ──────────────────────────────────────
    public ?int $programFilter = null;
    public ?int $trainingGroupId = null;

    // ─── Panel 3: Confirmation ───────────────────────────────
    public ?string $startDate = null;
    public string $notes = '';
    public bool $autoInvoice = true;

    // ─── Internal State ──────────────────────────────────────
    public ?array $participantData = null;
    public ?array $selectedGroupData = null;
    public ?array $pricePreview = null;
    public array $scheduleConflicts = [];
    public bool $isGroupFull = false;
    public ?string $priceError = null;

    public function mount(): void
    {
        $this->authorize('enrollments.create');

        $settings = app(SettingsService::class);
        $this->autoInvoice = (bool) $settings->get('auto_invoice_on_enrollment', true);
        $this->startDate = now()->toDateString();
    }

    public function rules(): array
    {
        // `exists:` is a raw table query — it never sees the branch global
        // scope, and both ids arrive from the browser. Without the branch
        // clause the rule cheerfully validates another branch's player into
        // this branch's group. Null only in all-branches mode, where there is
        // nothing to narrow to.
        $branchId = $this->getActiveBranchId();
        $inBranch = fn (string $table) => $branchId === null
            ? Rule::exists($table, 'id')
            : Rule::exists($table, 'id')->where('branch_id', $branchId);

        return [
            'participantId' => ['required', $inBranch('participants')],
            'trainingGroupId' => ['required', $inBranch('training_groups')],
            'startDate' => 'nullable|date|after_or_equal:today',
            'notes' => 'nullable|string|max:1000',
            'autoInvoice' => 'boolean',
        ];
    }

    public function messages(): array
    {
        return [
            'participantId.required' => 'اختيار المشترك مطلوب',
            'participantId.exists' => 'المشترك المختار غير موجود',
            'trainingGroupId.required' => 'اختيار المجموعة مطلوب',
            'trainingGroupId.exists' => 'المجموعة المختارة غير موجودة',
            'startDate.date' => 'تاريخ البدء غير صحيح',
            'startDate.after_or_equal' => 'تاريخ البدء يجب أن يكون اليوم أو بعده',
            'notes.max' => 'الملاحظات يجب ألا تتجاوز 1000 حرف',
        ];
    }

    // ─── Lifecycle Hooks ─────────────────────────────────────

    public function updatedParticipantId(?int $value): void
    {
        $this->reset(['trainingGroupId', 'selectedGroupData', 'pricePreview', 'scheduleConflicts', 'isGroupFull', 'priceError']);

        if (!$value) {
            $this->participantData = null;
            return;
        }

        $participant = Participant::with(['person', 'activeEnrollments.group.schedules', 'wallet'])
            ->find($value);

        if (!$participant) {
            $this->participantData = null;
            return;
        }

        $this->participantData = [
            'id' => $participant->id,
            'name' => $participant->person?->name_ar ?? $participant->person?->name ?? '',
            'status' => $participant->status->value,
            'age' => $participant->age,
            'enrollments_count' => $participant->activeEnrollments->count(),
            'wallet_balance' => $participant->wallet?->balance ?? 0,
            'is_blocked' => in_array($participant->status->value, ['frozen', 'suspended', 'blacklisted']),
        ];
    }

    public function updatedTrainingGroupId(?int $value): void
    {
        $this->reset(['selectedGroupData', 'pricePreview', 'scheduleConflicts', 'isGroupFull', 'priceError']);

        if (!$value) {
            return;
        }

        $group = TrainingGroup::with(['program', 'headTrainer', 'schedules' => fn ($q) => $q->where('is_active', true)])
            ->find($value);

        if (!$group) {
            return;
        }

        $this->selectedGroupData = [
            'id' => $group->id,
            'name' => $group->name_ar,
            'code' => $group->code,
            'status' => $group->status->value,
            'trainer' => $group->headTrainer?->name ?? '',
            'current_count' => $group->current_count,
            'max_capacity' => $group->max_capacity,
            'program_name' => $group->program?->name_ar ?? '',
            'schedules' => $group->schedules->map(fn ($s) => [
                'day' => $s->day_name,
                'day_of_week' => $s->day_of_week,
                'start_time' => $s->start_time,
                'end_time' => $s->end_time,
            ])->toArray(),
        ];

        $this->isGroupFull = $group->isFull();

        // Check schedule conflicts
        $this->detectScheduleConflicts($group);

        // Calculate price preview
        $this->calculatePrice($group);
    }

    public function updatedProgramFilter(): void
    {
        $this->reset(['trainingGroupId', 'selectedGroupData', 'pricePreview', 'scheduleConflicts', 'isGroupFull', 'priceError']);
    }

    // ─── Price Calculation ───────────────────────────────────

    private function calculatePrice(TrainingGroup $group): void
    {
        if (!$this->participantId || !$group->program) {
            $this->pricePreview = null;
            return;
        }

        try {
            $participant = Participant::find($this->participantId);
            $pricingService = app(PricingService::class);

            $result = $pricingService->calculate(
                priceable: $group->program,
                participant: $participant,
                branchId: $group->branch_id,
            );

            $this->pricePreview = [
                'base_amount' => $result->baseAmount,
                'final_amount' => $result->finalAmount,
                'total_discount' => $result->totalDiscount,
                'discount_percentage' => $result->discountPercentage(),
                'has_discount' => $result->hasDiscount(),
                'applied_rules' => $result->appliedRules,
            ];
            $this->priceError = null;
        } catch (DomainException $e) {
            $this->pricePreview = null;
            $this->priceError = $e->getMessage();
        }
    }

    // ─── Schedule Conflict Detection ─────────────────────────

    private function detectScheduleConflicts(TrainingGroup $selectedGroup): void
    {
        $this->scheduleConflicts = [];

        if (!$this->participantId) {
            return;
        }

        // Get participant's current active enrollments with their group schedules
        $existingEnrollments = Enrollment::with(['group.schedules' => fn ($q) => $q->where('is_active', true), 'group'])
            ->where('participant_id', $this->participantId)
            ->whereIn('status', ['active', 'pending'])
            ->get();

        $selectedSchedules = $selectedGroup->schedules->where('is_active', true);

        foreach ($existingEnrollments as $enrollment) {
            if (!$enrollment->group || !$enrollment->group->schedules) {
                continue;
            }

            foreach ($enrollment->group->schedules as $existingSchedule) {
                foreach ($selectedSchedules as $newSchedule) {
                    if ($this->schedulesOverlap($existingSchedule, $newSchedule)) {
                        $this->scheduleConflicts[] = [
                            'day' => $newSchedule->day_name,
                            'time' => substr($newSchedule->start_time, 0, 5) . ' - ' . substr($newSchedule->end_time, 0, 5),
                            'conflicting_group' => $enrollment->group->name_ar,
                        ];
                    }
                }
            }
        }
    }

    private function schedulesOverlap(TrainingSchedule $a, TrainingSchedule $b): bool
    {
        // Must be on same day
        if ($a->day_of_week !== $b->day_of_week) {
            return false;
        }

        // Check time overlap: A starts before B ends AND A ends after B starts
        return $a->start_time < $b->end_time && $a->end_time > $b->start_time;
    }

    // ─── Group Selection ─────────────────────────────────────

    public function selectGroup(int $groupId): void
    {
        $this->trainingGroupId = $groupId;
        $this->updatedTrainingGroupId($groupId);
    }

    // ─── Computed Properties ─────────────────────────────────

    #[Computed]
    public function searchResults(): \Illuminate\Support\Collection
    {
        if (strlen($this->participantSearch) < 2) {
            return collect();
        }

        return Participant::with('person')
            ->whereIn('status', ['registered', 'active', 'frozen', 'suspended', 'blacklisted'])
            ->where(function ($query) {
                $search = $this->participantSearch;
                $query->whereHas('person', function ($q) use ($search) {
                    $q->where('name_ar', 'like', "%{$search}%")
                      ->orWhere('name', 'like', "%{$search}%")
                      ->orWhere('phone', 'like', "%{$search}%");
                })
                ->orWhere('participant_number', 'like', "%{$search}%");
            })
            ->limit(10)
            ->get();
    }

    #[Computed]
    public function availablePrograms(): \Illuminate\Support\Collection
    {
        return TrainingProgram::where('registration_open', true)
            ->whereIn('status', ['active'])
            ->orderBy('name_ar')
            ->get(['id', 'name_ar', 'name']);
    }

    #[Computed]
    public function availableGroups(): \Illuminate\Support\Collection
    {
        $query = TrainingGroup::with(['program', 'headTrainer', 'schedules' => fn ($q) => $q->where('is_active', true)])
            ->whereIn('status', ['forming', 'active', 'full']);

        if ($this->programFilter) {
            $query->where('training_program_id', $this->programFilter);
        }

        return $query->orderBy('name_ar')->get();
    }

    // ─── Helpers ─────────────────────────────────────────────

    public function getStatusBadgeClass(string $status): string
    {
        return match ($status) {
            'active' => 'bg-green-100 text-green-800',
            'registered' => 'bg-blue-100 text-blue-800',
            'frozen' => 'bg-cyan-100 text-cyan-800',
            'suspended' => 'bg-orange-100 text-orange-800',
            'blacklisted' => 'bg-red-100 text-red-800',
            'forming' => 'bg-yellow-100 text-yellow-800',
            'full' => 'bg-red-100 text-red-800',
            default => 'bg-gray-100 text-gray-800',
        };
    }

    public function getStatusLabel(string $status): string
    {
        return match ($status) {
            'active' => 'نشط',
            'registered' => 'مسجل',
            'frozen' => 'متجمد',
            'suspended' => 'موقوف',
            'blacklisted' => 'محظور',
            'forming' => 'قيد التشكيل',
            'full' => 'ممتلئة',
            'on_hold' => 'معلقة',
            'completed' => 'مكتملة',
            default => $status,
        };
    }

    public function formatMoney(int $piasters): string
    {
        return number_format($piasters / 100, 2) . ' ج.م';
    }

    public function getCapacityPercentage(int $current, int $max): int
    {
        if ($max <= 0) {
            return 0;
        }
        return (int) min(100, round(($current / $max) * 100));
    }

    public function getCapacityColor(int $current, int $max): string
    {
        $pct = $this->getCapacityPercentage($current, $max);
        if ($pct >= 100) {
            return 'bg-red-500';
        }
        if ($pct >= 80) {
            return 'bg-amber-500';
        }
        return 'bg-green-500';
    }

    // ─── Form Submission ─────────────────────────────────────

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

        // Guard: blocked participant
        if ($this->participantData && $this->participantData['is_blocked']) {
            session()->flash('error', 'لا يمكن تسجيل هذا المشترك بسبب حالته الحالية');
            return;
        }

        try {
            $participant = Participant::findOrFail($this->participantId);
            $group = TrainingGroup::findOrFail($this->trainingGroupId);

            $options = [
                'start_date' => $this->startDate ?: null,
                'payment_status' => 'pending',
            ];

            // If auto-invoice is disabled, pass a flag so service won't create one
            if (!$this->autoInvoice) {
                $options['skip_invoice'] = true;
            }

            $enrollment = $service->enroll($participant, $group, auth()->user(), $options);

            session()->flash('success', 'تم التسجيل بنجاح في مجموعة "' . $group->name_ar . '"');
            $this->redirect(route('enrollments.list'), navigate: true);
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    // ─── Render ──────────────────────────────────────────────

    public function render()
    {
        return view('livewire.enrollments.enrollment-form');
    }
}
