<?php

namespace App\Livewire\Receptionist;

use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Financial\Services\PaymentService;
use App\Domain\Identity\Models\Guardian;
use App\Domain\Identity\Models\Person;
use App\Domain\Identity\Rules\FullName;
use App\Domain\Identity\Services\EgyptianNidDecoder;
use App\Domain\Identity\Services\PersonService;
use App\Domain\Participant\Services\ParticipantService;
use App\Domain\Pricing\Services\PricingService;
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 App\Domain\Training\Support\BillingCycle;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('تسجيل لاعب سابق')]
class RetroactiveEnrollmentWizard extends Component
{
    use UsesBranchScope;

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

    // Step 1: Participant info (create new)
    public string $participant_name_ar = '';
    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 bool $participant_nid_decoded = false;
    public bool $is_free = false;

    // Step 2: Guardian info
    public string $guardian_name_ar = '';
    public string $guardian_phone = '';
    public string $guardian_relation = 'father';

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

    // Step 4: Retroactive dates + months owed
    public string $actual_start_date = '';
    public array $monthStatuses = [];

    // Admin override
    public bool $priceOverrideEnabled = false;
    public string $priceOverrideInput = '';
    public string $priceOverrideReason = '';

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

    // Result
    public bool $completed = false;
    public ?string $enrollment_summary = null;
    public int $invoices_created = 0;
    public int $payments_recorded = 0;
    public ?string $participant_uuid = null;
    public ?string $participant_number = null;

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

        // This wizard back-dates a whole financial history — months of invoices
        // and payments — so it cannot run without knowing which branch it is
        // writing into. It used to fall back to the first active branch, which
        // filed that history against an arbitrary one. The id is not kept on the
        // component either: a public property holding it is settable from the
        // browser.
        $this->getActiveBranchIdOrFail();
    }

    /**
     * `exists:` runs a raw table query, so no Eloquent global scope reaches it,
     * and selected_program_id is a public property the browser can set.
     * A branch runs its own programmes, so only this branch's pass.
     */
    private function programExistsRule(): \Illuminate\Validation\Rules\Exists
    {
        $branchId = $this->getActiveBranchIdOrFail();

        return Rule::exists('training_programs', 'id')->where(
            fn ($q) => $q->where('branch_id', $branchId)
        );
    }

    public function rules(): array
    {
        return match ($this->currentStep) {
            1 => [
                'participant_name_ar' => ['required', 'string', 'min:3', 'max:100', new FullName],
                'participant_date_of_birth' => 'nullable|date|before:today',
                'participant_gender' => 'required|in:male,female',
                'participant_phone' => 'nullable|string|max:20',
                'participant_national_id' => 'nullable|string|size:14',
            ],
            2 => [
                'guardian_name_ar' => ['required', 'string', 'min:3', 'max:100', new FullName],
                'guardian_phone' => 'required|string|min:10|max:20',
                'guardian_relation' => 'required|in:father,mother,brother,sister,uncle,aunt,grandfather,grandmother,other',
            ],
            3 => [
                // activities are academy-level: they carry no branch.
                'selected_activity_id' => 'required|exists:activities,id',
                'selected_program_id' => ['required', $this->programExistsRule()],
            ],
            4 => ['actual_start_date' => 'required|date|before_or_equal:today'],
            5 => ['payment_method' => 'required_if:pay_now,true|in:cash,card,bank_transfer,wallet,online,cheque,other'],
            default => [],
        };
    }

    public function messages(): array
    {
        return [
            'participant_name_ar.required' => 'يرجى إدخال اسم اللاعب',
            'participant_name_ar.min' => 'اسم اللاعب قصير جداً',
            'participant_date_of_birth.before' => 'تاريخ الميلاد غير صحيح',
            'participant_national_id.size' => 'الرقم القومي يجب أن يكون 14 رقماً',
            'guardian_name_ar.required' => 'يرجى إدخال اسم ولي الأمر',
            'guardian_phone.required' => 'يرجى إدخال هاتف ولي الأمر',
            'guardian_phone.min' => 'رقم الهاتف قصير جداً',
            'selected_activity_id.required' => 'يرجى اختيار النشاط',
            'selected_program_id.required' => 'يرجى اختيار البرنامج',
            'actual_start_date.required' => 'يرجى تحديد تاريخ البدء الفعلي',
            'actual_start_date.before_or_equal' => 'تاريخ البدء لا يمكن أن يكون في المستقبل',
        ];
    }

    public function updatedParticipantNationalId(): void
    {
        $nid = trim($this->participant_national_id);
        if (strlen($nid) !== 14) {
            $this->participant_nid_decoded = false;
            return;
        }

        try {
            $decoder = app(EgyptianNidDecoder::class);
            $info = $decoder->decode($nid);
            $this->participant_date_of_birth = $info['date_of_birth'];
            $this->participant_gender = $info['gender'];
            $this->participant_nid_decoded = true;
        } catch (\Throwable) {
            $this->participant_nid_decoded = false;
        }
    }

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

    public function updatedActualStartDate(): void
    {
        $this->calculateMonthsOwed();
    }

    public function calculateMonthsOwed(): void
    {
        if (!$this->actual_start_date || !$this->selected_program_id) {
            $this->monthStatuses = [];
            return;
        }

        $start = Carbon::parse($this->actual_start_date)->startOfMonth();
        $now = now()->startOfMonth();

        $months = [];
        $current = $start->copy();
        while ($current->lte($now)) {
            $key = $current->format('Y-m');
            $months[$key] = 'unpaid';
            $current->addMonth();
        }

        foreach ($months as $key => $default) {
            if (!isset($this->monthStatuses[$key])) {
                $this->monthStatuses[$key] = $default;
            }
        }

        $this->monthStatuses = array_intersect_key($this->monthStatuses, $months);
    }

    public function setMonthStatus(string $month, string $status): void
    {
        if (isset($this->monthStatuses[$month])) {
            $this->monthStatuses[$month] = $status;
        }
    }

    #[Computed]
    public function monthlyPrice(): int
    {
        if (!$this->selected_program_id) {
            return 0;
        }

        try {
            $program = TrainingProgram::find($this->selected_program_id);
            if (!$program) return 0;

            $pricingService = app(PricingService::class);
            $result = $pricingService->calculate(
                priceable: $program,
                participant: null,
                branchId: $this->getActiveBranchIdOrFail(),
            );
            return $result->finalAmount;
        } catch (\Throwable) {
            return 0;
        }
    }

    #[Computed]
    public function unpaidMonthsCount(): int
    {
        return collect($this->monthStatuses)->filter(fn ($s) => $s === 'unpaid')->count();
    }

    #[Computed]
    public function totalOwed(): int
    {
        if ($this->priceOverrideEnabled && $this->priceOverrideInput !== '') {
            return max(0, (int) round((float) $this->priceOverrideInput * 100));
        }
        return $this->monthlyPrice * $this->unpaidMonthsCount;
    }

    public function nextStep(): void
    {
        $this->validate();

        if ($this->currentStep === 4) {
            $this->calculateMonthsOwed();
        }

        $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 confirm(): void
    {
        $this->validate([
            'participant_name_ar' => ['required', 'string', 'min:3', 'max:100', new FullName],
            'guardian_name_ar' => ['required', 'string', 'min:3', 'max:100', new FullName],
            'guardian_phone' => 'required|string|min:10|max:20',
            'selected_program_id' => ['required', $this->programExistsRule()],
            'actual_start_date' => 'required|date|before_or_equal:today',
            'payment_method' => 'required_if:pay_now,true|in:cash,card,bank_transfer,wallet,online,cheque,other',
        ]);

        $actor = auth()->user();

        // Resolved once, here, and passed into the transaction: every invoice
        // and every payment below is stamped with it, and it must be the branch
        // the request is actually in rather than anything the browser sent.
        $branchId = $this->getActiveBranchIdOrFail();

        // Duplicate NID check
        if ($this->participant_national_id) {
            $existingPerson = Person::where('national_id', $this->participant_national_id)->first();
            if ($existingPerson) {
                session()->flash('error', __('يوجد شخص مسجل بنفس الرقم القومي: ') . $existingPerson->name_ar);
                return;
            }
        }

        // Price validation — don't allow 0-amount invoices unless explicitly overridden (skip for free players)
        if (!$this->is_free) {
            $effectivePrice = $this->monthlyPrice;
            if ($this->priceOverrideEnabled && $this->priceOverrideInput !== '' && $actor->is_super_admin) {
                $effectivePrice = max(0, (int) round((float) $this->priceOverrideInput * 100));
            }
            if ($effectivePrice <= 0 && $this->unpaidMonthsCount > 0 && !$this->priceOverrideEnabled) {
                session()->flash('error', __('لا يوجد سعر محدد لهذا البرنامج — يرجى إضافة سعر أو استخدام تعديل السعر'));
                return;
            }
        }

        try {
            DB::transaction(function () use ($actor, $branchId) {
                $personService = app(PersonService::class);
                $participantService = app(ParticipantService::class);
                $enrollmentService = app(EnrollmentService::class);
                $invoiceService = app(InvoiceService::class);
                $paymentService = app(PaymentService::class);

                // 1. Create guardian Person (or find by phone)
                $guardianPerson = Person::where('phone', $this->guardian_phone)->first();
                if (!$guardianPerson) {
                    $guardianPerson = $personService->create([
                        'name_ar' => $this->guardian_name_ar,
                        'name' => $this->guardian_name_ar,
                        'phone' => $this->guardian_phone,
                    ], $actor);
                }

                // 2. Create or find 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 Person record
                $participantPerson = $personService->create([
                    'name_ar' => $this->participant_name_ar,
                    '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
                $participant = $participantService->register([
                    'person_id' => $participantPerson->id,
                    'branch_id' => $branchId,
                    'registration_source' => 'walk_in',
                    'primary_guardian_id' => $guardian->id,
                    'primary_activity_id' => $this->selected_activity_id,
                    'is_free' => $this->is_free,
                ], $actor);

                // 5. Link guardian to participant
                $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 with retroactive start date
                // Pass skip_auto_invoice to prevent auto_invoice_on_enrollment from creating a duplicate
                $program = TrainingProgram::findOrFail($this->selected_program_id);
                $enrollment = $enrollmentService->enrollInProgram(
                    $participant,
                    $program,
                    $actor,
                    [
                        'start_date' => $this->actual_start_date,
                        'payment_status' => 'pending',
                        'skip_auto_invoice' => true,
                    ]
                );

                // 7. Calculate effective per-month price (skip all invoicing for free players)
                $invoicesCreated = 0;
                $paymentsRecorded = 0;

                if ($this->is_free) {
                    $enrollment->update([
                        'next_billing_date' => null,
                        'payment_status' => 'waived',
                    ]);
                } else {

                $perMonth = $this->monthlyPrice;

                $unpaidCount = $this->unpaidMonthsCount;
                $overrideActive = $this->priceOverrideEnabled && $this->priceOverrideInput !== '' && $actor->is_super_admin;
                if ($overrideActive && $unpaidCount > 0) {
                    $overrideTotal = max(0, (int) round((float) $this->priceOverrideInput * 100));
                    $perMonth = (int) floor($overrideTotal / $unpaidCount);

                    Log::channel('audit')->info('retroactive_enrollment_price_override', [
                        'actor_id' => $actor->id,
                        'actor_name' => $actor->name,
                        'participant_name' => $this->participant_name_ar,
                        'program' => $program->name_ar,
                        'original_per_month' => $this->monthlyPrice,
                        'override_total' => $overrideTotal,
                        'effective_per_month' => $perMonth,
                        'months_count' => $unpaidCount,
                        'reason' => $this->priceOverrideReason,
                    ]);
                }

                // 8. Generate invoices per month
                $totalRetroactiveRevenue = 0;

                $unpaidIndex = 0;
                $unpaidTotal = collect($this->monthStatuses)->filter(fn ($s) => $s === 'unpaid')->count();
                $remainder = 0;
                if ($overrideActive && $unpaidTotal > 0) {
                    $overrideTotalCalc = max(0, (int) round((float) $this->priceOverrideInput * 100));
                    $remainder = $overrideTotalCalc - ($perMonth * $unpaidTotal);
                }

                foreach ($this->monthStatuses as $monthKey => $status) {
                    $monthDate = Carbon::parse($monthKey . '-01');

                    if ($status === 'paid_outside') {
                        $invoice = $invoiceService->create([
                            'academy_id' => $participant->academy_id,
                            'branch_id' => $branchId,
                            'billable_type' => $participant->getMorphClass(),
                            'billable_id' => $participant->id,
                            'total_amount' => $perMonth,
                            'subtotal_amount' => $perMonth,
                            'discount_amount' => 0,
                            'tax_amount' => 0,
                            'service_fee_amount' => 0,
                            'due_date' => $monthDate->copy()->addDays(7)->toDateString(),
                            'contact_name' => $participantPerson->name_ar,
                            'notes' => 'اشتراك ' . $program->name_ar . ' — ' . $this->getArabicMonth($monthDate),
                            'metadata' => ['retroactive' => true, 'month' => $monthKey, 'fee_deferred_to_registration_month' => true],
                        ], [
                            [
                                'description' => 'اشتراك: ' . $program->name_ar . ' (' . $this->getArabicMonth($monthDate) . ')',
                                'quantity' => 1,
                                'unit_price' => $perMonth,
                                'discount_amount' => 0,
                                'tax_amount' => 0,
                            ],
                        ], $actor);

                        $invoice->update(['status' => InvoiceStatus::Sent]);

                        $paymentService->recordPayment([
                            'invoice_id' => $invoice->id,
                            'branch_id' => $branchId,
                            'amount' => $perMonth,
                            'method' => 'cash',
                            'direction' => 'inbound',
                            'currency' => 'EGP',
                            'payment_date' => $monthDate->copy()->addDays(1)->toDateString(),
                            'notes' => 'مدفوع خارج السيستم — ' . $this->getArabicMonth($monthDate),
                        ], $actor);

                        $totalRetroactiveRevenue += $perMonth;
                        $invoicesCreated++;
                        $paymentsRecorded++;
                    } elseif ($status === 'unpaid') {
                        $unpaidIndex++;
                        $thisMonthAmount = $perMonth;
                        if ($unpaidIndex === $unpaidTotal && $remainder > 0) {
                            $thisMonthAmount += $remainder;
                        }
                        $invoice = $invoiceService->create([
                            'academy_id' => $participant->academy_id,
                            'branch_id' => $branchId,
                            'billable_type' => $participant->getMorphClass(),
                            'billable_id' => $participant->id,
                            'total_amount' => $thisMonthAmount,
                            'subtotal_amount' => $thisMonthAmount,
                            'discount_amount' => 0,
                            'tax_amount' => 0,
                            'service_fee_amount' => 0,
                            'due_date' => $monthDate->copy()->addDays(7)->toDateString(),
                            'contact_name' => $participantPerson->name_ar,
                            'notes' => 'اشتراك ' . $program->name_ar . ' — ' . $this->getArabicMonth($monthDate),
                            'metadata' => ['retroactive' => true, 'month' => $monthKey, 'fee_deferred_to_registration_month' => true],
                        ], [
                            [
                                'description' => 'اشتراك: ' . $program->name_ar . ' (' . $this->getArabicMonth($monthDate) . ')',
                                'quantity' => 1,
                                'unit_price' => $thisMonthAmount,
                                'discount_amount' => 0,
                                'tax_amount' => 0,
                            ],
                        ], $actor);

                        $invoice->update(['status' => InvoiceStatus::Sent]);
                        $totalRetroactiveRevenue += $thisMonthAmount;
                        $invoicesCreated++;

                        if ($this->pay_now && $thisMonthAmount > 0) {
                            $paymentService->recordPayment([
                                'invoice_id' => $invoice->id,
                                'branch_id' => $branchId,
                                'amount' => $thisMonthAmount,
                                'method' => $this->payment_method,
                                'direction' => 'inbound',
                                'currency' => 'EGP',
                                'payment_date' => now()->toDateString(),
                                'notes' => 'دفع بأثر رجعي — ' . $this->getArabicMonth($monthDate),
                            ], $actor);
                            $paymentsRecorded++;
                        }
                    }
                }

                // 9. Platform fee invoice — charged TODAY for ALL retroactive months
                $platformFeeService = app(PlatformFeeService::class);
                if ($platformFeeService->isActive() && $totalRetroactiveRevenue > 0) {
                    $totalPlatformFee = $platformFeeService->calculate($totalRetroactiveRevenue);

                    if ($totalPlatformFee > 0) {
                        $monthCount = count($this->monthStatuses);
                        $feeInvoice = $invoiceService->create([
                            'academy_id' => $participant->academy_id,
                            'branch_id' => $branchId,
                            'billable_type' => $participant->getMorphClass(),
                            'billable_id' => $participant->id,
                            'total_amount' => $totalPlatformFee,
                            'subtotal_amount' => 0,
                            'discount_amount' => 0,
                            'tax_amount' => 0,
                            'service_fee_amount' => $totalPlatformFee,
                            'due_date' => now()->addDays(7)->toDateString(),
                            'contact_name' => $participantPerson->name_ar,
                            'notes' => 'رسوم خدمة — تسجيل بأثر رجعي (' . $monthCount . ' شهور) — ' . $program->name_ar,
                            'metadata' => [
                                'retroactive_platform_fee' => true,
                                'covers_months' => array_keys($this->monthStatuses),
                                'total_revenue_base' => $totalRetroactiveRevenue,
                                'fee_percentage' => $platformFeeService->getPercentage(),
                                'registered_at' => now()->toIso8601String(),
                            ],
                        ], [
                            [
                                'description' => 'رسوم خدمة المنصة — تسجيل بأثر رجعي (' . $monthCount . ' شهور)',
                                'quantity' => 1,
                                'unit_price' => $totalPlatformFee,
                                'discount_amount' => 0,
                                'tax_amount' => 0,
                            ],
                        ], $actor);

                        $feeInvoice->update(['status' => InvoiceStatus::Sent]);
                        $invoicesCreated++;
                    }
                }

                // 10. Every month up to and including this one has just been
                // raised, so hand over to the next cycle. Same rule, same
                // implementation as the nightly command and the payment wizard.
                $enrollment->update([
                    'next_billing_date' => BillingCycle::next()->toDateString(),
                    'last_billed_at' => BillingCycle::startOf()->toDateString(),
                ]);

                } // end else (!is_free)

                $this->invoices_created = $invoicesCreated;
                $this->payments_recorded = $paymentsRecorded;
                $this->participant_uuid = $participant->uuid;
                $this->participant_number = $participant->participant_number;
                $this->enrollment_summary = $program->name_ar;
                $this->completed = true;
                $this->currentStep = 6;
            });

            session()->flash('success', __('تم تسجيل اللاعب بنجاح بأثر رجعي'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Throwable $e) {
            Log::error('Retroactive enrollment failed', ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
            session()->flash('error', __('حدث خطأ غير متوقع'));
        }
    }

    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')
                ->where('registration_open', true)
                ->orderBy('name_ar')
                ->get();
        }

        return view('livewire.receptionist.retroactive-enrollment-wizard', [
            'activities' => $activities,
            'programs' => $programs,
            'isSuperAdmin' => auth()->user()?->is_super_admin ?? false,
        ]);
    }

    private function getArabicMonth(Carbon $date): string
    {
        $months = [
            1 => 'يناير', 2 => 'فبراير', 3 => 'مارس', 4 => 'أبريل',
            5 => 'مايو', 6 => 'يونيو', 7 => 'يوليو', 8 => 'أغسطس',
            9 => 'سبتمبر', 10 => 'أكتوبر', 11 => 'نوفمبر', 12 => 'ديسمبر',
        ];
        return $months[$date->month] . ' ' . $date->year;
    }
}
