<?php

namespace App\Livewire\Receptionist;

use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Financial\Services\PaymentService;
use App\Domain\Identity\Models\Branch;
use App\Domain\Identity\Models\Guardian;
use App\Domain\Identity\Models\Person;
use App\Domain\Identity\Services\PersonService;
use App\Domain\Participant\Models\Participant;
use App\Domain\Participant\Services\DuplicateDetectionService;
use App\Domain\Participant\Services\ParticipantService;
use App\Domain\Pricing\Models\BasePrice;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Services\PlatformFeeService;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\Activity;
use App\Domain\Training\Models\TrainingProgram;
use App\Domain\Training\Services\EnrollmentService;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('تسجيل جديد')]
class NewRegistrationWizard extends Component
{
    use UsesBranchScope;

    public ?int $branchId = null;

    public int $currentStep = 1;
    public int $totalSteps = 6;

    // Step 1: Guardian info
    public string $guardian_name_ar = '';
    public string $guardian_name = '';
    public string $guardian_phone = '';
    public string $guardian_national_id = '';
    public string $guardian_relation = 'father';

    // Step 2: Participant (the actual player/child)
    public string $participant_name_ar = '';
    public string $participant_name = '';
    public ?string $participant_date_of_birth = null;
    public string $participant_gender = 'male';
    public string $participant_phone = '';
    public string $participant_national_id = '';
    public string $participant_medical_notes = '';
    public string $membership_type = 'non_member';
    public string $membership_id = '';

    // Step 3: Program selection
    public ?int $selected_activity_id = null;
    public ?int $selected_program_id = null;

    // Step 5: Payment
    public bool $pay_now = false;
    public string $payment_method = 'cash';

    // Duplicate detection
    public array $potentialDuplicates = [];
    public bool $duplicateCheckDone = false;
    public ?int $useExistingPersonId = null;

    // Result
    public bool $completed = false;
    public ?string $participant_number = null;
    public ?string $participant_uuid = null;
    public ?string $enrollment_summary = null;
    public ?int $invoice_amount = null;
    public ?string $invoice_number = null;
    public ?int $invoiceId = null;
    public ?string $invoice_uuid = null;
    public bool $payment_recorded = false;

    // Pre-flight errors — blocks wizard from starting
    public array $systemErrors = [];

    public function mount(): void
    {
        $this->authorize('participants.create');
        $this->branchId = $this->getActiveBranchId() ?? Branch::where('is_active', true)->first()?->id;
        $this->runPreflightChecks();
    }

    private function runPreflightChecks(): void
    {
        $errors = [];

        if (!app('current_academy')) {
            $errors[] = 'لا توجد أكاديمية مفعّلة — تواصل مع المسؤول';
        }

        if (!$this->branchId) {
            $errors[] = 'لا يوجد فرع محدد — اختر فرعاً من الإعدادات';
        }

        $hasActivities = Activity::where('is_active', true)->exists();
        if (!$hasActivities) {
            $errors[] = 'لا توجد أنشطة مفعّلة — يجب إنشاء نشاط واحد على الأقل';
        }

        $hasPrograms = TrainingProgram::where('status', 'active')
            ->whereHas('groups', fn ($q) => $q->where('branch_id', $this->branchId)->whereIn('status', ['active', 'forming']))
            ->exists();
        if (!$hasPrograms) {
            $errors[] = 'لا توجد برامج متاحة بها مجموعات نشطة في هذا الفرع';
        }

        $hasPrices = BasePrice::where('is_active', true)
            ->where('priceable_type', TrainingProgram::class)
            ->where('effective_from', '<=', now())
            ->where(fn ($q) => $q->whereNull('effective_to')->orWhere('effective_to', '>=', now()))
            ->exists();
        if (!$hasPrices) {
            $errors[] = 'لا توجد أسعار محددة لأي برنامج — يجب تحديد سعر أساسي أولاً';
        }

        $this->systemErrors = $errors;
    }

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

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

        // Duplicate check on step 1 — if duplicates found, stay on step 1
        if ($this->currentStep === 1 && !$this->duplicateCheckDone) {
            $this->checkDuplicates();
            return;
        }

        // Price guard on step 3 — block if no base price for selected program
        if ($this->currentStep === 3 && $this->selected_program_id) {
            $program = TrainingProgram::find($this->selected_program_id);
            if ($program && $this->resolveProgramFee($program) === 0) {
                $this->addError('selected_program_id', 'لا يوجد سعر محدد لهذا البرنامج — تواصل مع المسؤول');
                return;
            }
        }

        $this->currentStep = min($this->currentStep + 1, $this->totalSteps);
    }

    private function rulesForStep(int $step): array
    {
        return match ($step) {
            1 => [
                'guardian_name_ar' => 'required|string|max:255',
                'guardian_phone' => 'required|string|max:20',
                'guardian_national_id' => 'nullable|string|max:14',
                'guardian_relation' => 'required|in:father,mother,brother,sister,uncle,aunt,grandfather,grandmother,guardian,other',
            ],
            2 => [
                'participant_name_ar' => 'required|string|max:255',
                'participant_date_of_birth' => 'required|date|before:today',
                'participant_gender' => 'required|in:male,female',
                'participant_phone' => 'nullable|string|max:20',
                'participant_national_id' => 'nullable|string|max:14',
                'participant_medical_notes' => 'nullable|string|max:1000',
                'membership_type' => 'required|in:member,non_member',
                'membership_id' => 'required_if:membership_type,member|nullable|string|max:50',
            ],
            3 => [
                'selected_activity_id' => 'required|exists:activities,id',
                'selected_program_id' => 'required|exists:training_programs,id',
            ],
            5 => [
                'payment_method' => 'required_if:pay_now,true|in:cash,card,wallet',
            ],
            default => [],
        };
    }

    public function messages(): array
    {
        return [
            'guardian_name_ar.required' => 'اسم ولي الأمر مطلوب',
            'guardian_phone.required' => 'رقم الهاتف مطلوب',
            'guardian_relation.required' => 'صلة القرابة مطلوبة',
            'guardian_relation.in' => 'صلة القرابة غير صالحة',
            'participant_name_ar.required' => 'اسم المشترك مطلوب',
            'participant_date_of_birth.required' => 'تاريخ الميلاد مطلوب',
            'participant_date_of_birth.before' => 'تاريخ الميلاد يجب أن يكون في الماضي',
            'participant_gender.required' => 'الجنس مطلوب',
            'participant_gender.in' => 'الجنس غير صالح',
            'selected_activity_id.required' => 'يرجى اختيار النشاط',
            'selected_activity_id.exists' => 'النشاط المختار غير موجود',
            'selected_program_id.required' => 'يرجى اختيار البرنامج',
            'selected_program_id.exists' => 'البرنامج المختار غير موجود',
            'membership_type.required' => 'نوع العضوية مطلوب',
            'membership_type.in' => 'نوع العضوية غير صالح',
            'membership_id.required_if' => 'رقم عضوية النادي مطلوب للأعضاء',
            'payment_method.required_if' => 'يرجى اختيار طريقة الدفع',
            'payment_method.in' => 'طريقة الدفع غير صالحة',
        ];
    }

    public function checkDuplicates(): void
    {
        $service = app(DuplicateDetectionService::class);

        $matches = $service->findPotentialDuplicates(
            name: $this->guardian_name_ar,
            phone: $this->guardian_phone ?: null,
        );

        $highConfidence = $matches->filter(fn ($m) => $m['confidence'] >= 80);

        $this->potentialDuplicates = $highConfidence->map(fn ($m) => [
            'person_id' => $m['person']->id,
            'name' => $m['person']->name_ar ?: $m['person']->name,
            'phone' => $m['person']->phone,
            'confidence' => $m['confidence'],
            'match_reason' => $m['match_reason'],
            'has_participant' => $m['person']->participant !== null,
            'participant_number' => $m['person']->participant?->participant_number,
        ])->toArray();

        $this->duplicateCheckDone = true;

        if (empty($this->potentialDuplicates)) {
            $this->currentStep = min($this->currentStep + 1, $this->totalSteps);
        }
    }

    public function proceedDespiteDuplicates(): void
    {
        $this->potentialDuplicates = [];
        $this->currentStep = min($this->currentStep + 1, $this->totalSteps);
    }

    public function useExistingPerson(int $personId): void
    {
        $this->useExistingPersonId = $personId;
        $this->potentialDuplicates = [];
        $this->currentStep = min($this->currentStep + 1, $this->totalSteps);
    }

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

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

    public function updatedGuardianNameAr(): void
    {
        $this->resetDuplicateCheck();
    }

    public function updatedGuardianPhone(): void
    {
        $this->resetDuplicateCheck();
    }

    private function resetDuplicateCheck(): void
    {
        $this->duplicateCheckDone = false;
        $this->potentialDuplicates = [];
        $this->useExistingPersonId = null;
    }

    public function updatedSelectedActivityId(): void
    {
        $this->selected_program_id = null;
    }

    public function confirm(): void
    {
        try {
            DB::transaction(function () {
                $actor = auth()->user();
                $personService = app(PersonService::class);
                $participantService = app(ParticipantService::class);
                $enrollmentService = app(EnrollmentService::class);
                $invoiceService = app(InvoiceService::class);
                $paymentService = app(PaymentService::class);

                // 1. Create or reuse guardian's Person record
                if ($this->useExistingPersonId) {
                    $guardianPerson = Person::findOrFail($this->useExistingPersonId);
                } else {
                    $guardianPerson = Person::where('phone', $this->guardian_phone)->first();
                    if (!$guardianPerson) {
                        $guardianPerson = $personService->create([
                            'name_ar' => $this->guardian_name_ar,
                            'name' => $this->guardian_name ?: $this->guardian_name_ar,
                            'phone' => $this->guardian_phone,
                            'national_id' => $this->guardian_national_id ?: null,
                        ], $actor);
                    }
                }

                // 2. Create or find the Guardian record
                $guardian = Guardian::firstOrCreate(
                    ['person_id' => $guardianPerson->id],
                    [
                        'academy_id' => app('current_academy')->id,
                        'relationship_type' => $this->guardian_relation,
                        'is_emergency_contact' => true,
                        'is_financial_responsible' => true,
                        'can_pickup' => true,
                    ]
                );

                // 3. Create participant's Person record
                $participantPerson = $personService->create([
                    'name_ar' => $this->participant_name_ar,
                    'name' => $this->participant_name ?: $this->participant_name_ar,
                    'date_of_birth' => $this->participant_date_of_birth,
                    'gender' => $this->participant_gender,
                    'phone' => $this->participant_phone ?: null,
                    'national_id' => $this->participant_national_id ?: null,
                    'medical_notes' => $this->participant_medical_notes ?: null,
                ], $actor);

                // 4. Create Participant record via service
                $participant = $participantService->register([
                    'person_id' => $participantPerson->id,
                    'branch_id' => $this->branchId,
                    'registration_source' => 'walk_in',
                    'primary_guardian_id' => $guardian->id,
                    'primary_activity_id' => $this->selected_activity_id,
                    'membership_type' => $this->membership_type,
                    'membership_id' => $this->membership_type === 'member' ? $this->membership_id : null,
                ], $actor);

                // 5. Link guardian to participant via pivot
                $participant->guardians()->attach($guardian->id, [
                    'relationship_type' => $this->guardian_relation,
                    'is_primary' => true,
                    'is_emergency_contact' => true,
                    'can_pickup' => true,
                    'receives_notifications' => true,
                    'can_authorize_payment' => true,
                ]);

                // 6. Enroll in program
                $program = TrainingProgram::findOrFail($this->selected_program_id);
                $enrollment = $enrollmentService->enrollInProgram(
                    $participant,
                    $program,
                    $actor
                );

                // 7. Look up the price for this program + calculate platform fee
                $feeAmount = $this->resolveProgramFee($program);
                $platformFeeService = app(PlatformFeeService::class);
                $serviceFee = $platformFeeService->calculate($feeAmount);
                $totalWithFee = $feeAmount + $serviceFee;

                // 8. Create invoice if there's a fee
                $invoice = null;
                if ($feeAmount > 0) {
                    $invoice = $invoiceService->create([
                        'academy_id' => app('current_academy')->id,
                        'number' => $invoiceService->generateNumber(app('current_academy')->id),
                        'type' => 'standard',
                        'billable_type' => Participant::class,
                        'billable_id' => $participant->id,
                        'contact_name' => $this->guardian_name_ar,
                        'contact_phone' => $this->guardian_phone,
                        'subtotal_amount' => $feeAmount,
                        'discount_amount' => 0,
                        'tax_amount' => 0,
                        'service_fee_amount' => $serviceFee,
                        'total_amount' => $totalWithFee,
                        'currency' => 'EGP',
                        'issue_date' => now()->toDateString(),
                        'due_date' => now()->addDays(7)->toDateString(),
                        'notes' => 'اشتراك: ' . $program->name_ar,
                    ], [
                        [
                            'description' => $program->name_ar,
                            'quantity' => 1,
                            'unit_price' => $feeAmount,
                            'discount_amount' => 0,
                            'tax_amount' => 0,
                        ],
                    ], $actor);

                    // Send the invoice (mark as sent)
                    $invoice->update(['status' => 'sent']);

                    // Link enrollment to invoice
                    $enrollment->update(['invoice_id' => $invoice->id]);

                    $this->invoice_amount = $invoice->total_amount;
                    $this->invoice_number = $invoice->number;
                    $this->invoiceId = $invoice->id;

                    // 9. Record payment if paying now
                    if ($this->pay_now) {
                        $paymentService->recordPayment([
                            'academy_id' => app('current_academy')->id,
                            'branch_id' => $this->branchId,
                            'invoice_id' => $invoice->id,
                            'reference' => 'PAY-' . now()->format('YmdHis') . '-' . $participant->id,
                            'direction' => 'inbound',
                            'method' => $this->payment_method,
                            'payer_type' => Participant::class,
                            'payer_id' => $participant->id,
                            'amount' => $invoice->total_amount,
                            'currency' => 'EGP',
                            'payment_date' => now()->toDateString(),
                            'notes' => 'دفع اشتراك: ' . $program->name_ar,
                        ], $actor);

                        $this->payment_recorded = true;
                        $enrollment->update(['payment_status' => 'paid']);
                    }
                }

                $this->completed = true;
                $this->participant_number = $participant->participant_number;
                $this->participant_uuid = $participant->uuid;
                $this->enrollment_summary = $program->name_ar . ' — ' . ($enrollment->group?->name_ar ?? '');
                if ($invoice) {
                    $this->invoice_uuid = $invoice->uuid;
                }
                $this->currentStep = 6;
            });

            session()->flash('success', __('تم التسجيل بنجاح'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Throwable $e) {
            session()->flash('error', 'حدث خطأ: ' . $e->getMessage());
        }
    }

    private function resolveProgramFee(TrainingProgram $program): int
    {
        $query = BasePrice::where('priceable_type', TrainingProgram::class)
            ->where('priceable_id', $program->id)
            ->where('is_active', true)
            ->where('effective_from', '<=', now())
            ->where(fn ($q) => $q->whereNull('effective_to')->orWhere('effective_to', '>=', now()))
            ->forBranch($this->branchId);

        // Try membership-specific price first
        $specificPrice = (clone $query)
            ->whereJsonContains('metadata->membership_type', $this->membership_type)
            ->orderByDesc('priority')
            ->first();

        if ($specificPrice) {
            return $specificPrice->amount;
        }

        // Fall back to generic price (no membership_type in metadata)
        $genericPrice = $query
            ->where(fn ($q) => $q
                ->whereNull('metadata->membership_type')
                ->orWhereRaw("metadata->>'membership_type' = ''")
                ->orWhereRaw("metadata = '{}'::jsonb")
            )
            ->orderByDesc('priority')
            ->first();

        return $genericPrice?->amount ?? 0;
    }

    public function getSelectedProgramProperty(): ?TrainingProgram
    {
        if (!$this->selected_program_id) {
            return null;
        }

        return TrainingProgram::with('activity')->find($this->selected_program_id);
    }

    public function getSelectedProgramFeeProperty(): int
    {
        if (!$this->selected_program_id) {
            return 0;
        }

        $program = TrainingProgram::find($this->selected_program_id);
        return $program ? $this->resolveProgramFee($program) : 0;
    }

    public function getPlatformFeeProperty(): int
    {
        return app(PlatformFeeService::class)->calculate($this->selectedProgramFee);
    }

    public function getTotalWithFeeProperty(): int
    {
        return $this->selectedProgramFee + $this->platformFee;
    }

    public function getInvoiceForPrintProperty(): ?Invoice
    {
        if (!$this->invoiceId) {
            return null;
        }
        return Invoice::with('items')->find($this->invoiceId);
    }

    public function render()
    {
        $activities = Activity::where('is_active', true)->orderBy('name_ar')->get();
        $programs = collect();

        if ($this->selected_activity_id) {
            $programs = TrainingProgram::where('activity_id', $this->selected_activity_id)
                ->where('status', 'active')
                ->whereHas('groups', fn ($q) => $q->where('branch_id', $this->branchId)->whereIn('status', ['active', 'forming']))
                ->orderBy('name_ar')
                ->get();
        }

        return view('livewire.receptionist.new-registration-wizard', [
            'activities' => $activities,
            'programs' => $programs,
            'relationOptions' => [
                'father' => 'أب',
                'mother' => 'أم',
                'brother' => 'أخ',
                'sister' => 'أخت',
                'uncle' => 'عم/خال',
                'aunt' => 'عمة/خالة',
                'grandfather' => 'جد',
                'grandmother' => 'جدة',
                'guardian' => 'وصي',
                'other' => 'أخرى',
            ],
        ]);
    }
}
