<?php

namespace App\Livewire\Receptionist;

use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Models\Installment;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Financial\Services\PaymentService;
use App\Domain\Participant\Models\Participant;
use App\Domain\Pricing\Services\PricingService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Enums\EnrollmentStatus;
use App\Domain\Training\Enums\RenewalPolicy;
use App\Domain\Training\Models\Enrollment;
use Illuminate\Support\Facades\Log;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('تحصيل مدفوعات')]
class CollectPaymentWizard extends Component
{
    use UsesBranchScope;

    public ?int $branchId = null;

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

    // Step 1: Search
    public string $search = '';
    public ?int $selected_participant_id = null;
    public ?string $selected_participant_name = null;

    // Step 2: Invoice selection
    public ?int $selected_invoice_id = null;

    // Step 3: Payment details
    public string $payment_amount_display = '';
    public string $payment_method = 'cash';
    public string $payment_notes = '';

    // Result
    public bool $completed = false;
    public ?string $receipt_number = null;
    public ?string $last_payment_uuid = null;
    public int $paid_amount = 0;
    public ?string $participant_uuid = null;
    public ?string $invoice_uuid = null;

    public function mount(?string $participant = null): void
    {
        $this->authorize('invoices.create');
        $this->branchId = $this->getActiveBranchIdOrFail();

        if ($participant) {
            $p = Participant::with('person')->where('uuid', $participant)->first();
            if ($p) {
                $this->selected_participant_id = $p->id;
                $this->selected_participant_name = $p->person?->name_ar ?? $p->person?->name ?? '';
                $this->generatePendingRenewals();
                $this->currentStep = 2;
            }
        }
    }

    public function rules(): array
    {
        return match ($this->currentStep) {
            1 => [
                'selected_participant_id' => 'required|exists:participants,id',
            ],
            2 => [
                'selected_invoice_id' => 'required|exists:invoices,id',
            ],
            3 => [
                'payment_amount_display' => 'required|numeric|min:0.01',
                'payment_method' => 'required|in:cash,card,wallet',
                'payment_notes' => 'nullable|string|max:500',
            ],
            default => [],
        };
    }

    public function messages(): array
    {
        return [
            'selected_participant_id.required' => 'يرجى اختيار المشترك',
            'selected_participant_id.exists' => 'المشترك المختار غير موجود',
            'selected_invoice_id.required' => 'يرجى اختيار الفاتورة',
            'selected_invoice_id.exists' => 'الفاتورة المختارة غير موجودة',
            'payment_amount_display.required' => 'المبلغ مطلوب',
            'payment_amount_display.numeric' => 'المبلغ يجب أن يكون رقمًا',
            'payment_amount_display.min' => 'المبلغ يجب أن يكون أكبر من صفر',
            'payment_method.required' => 'يرجى اختيار طريقة الدفع',
            'payment_method.in' => 'طريقة الدفع غير صالحة',
        ];
    }

    public function selectParticipant(int $id, string $name): void
    {
        $this->selected_participant_id = $id;
        $this->selected_participant_name = $name;
        $this->generatePendingRenewals();
    }

    public function generateRenewalForEnrollment(int $enrollmentId): void
    {
        $enrollment = Enrollment::with(['program', 'participant.person', 'group'])->find($enrollmentId);
        if (!$enrollment || $enrollment->participant_id !== $this->selected_participant_id) {
            return;
        }

        try {
            $invoiceService = app(InvoiceService::class);
            $pricingService = app(PricingService::class);

            $participant = $enrollment->participant;
            $program = $enrollment->program;
            $group = $enrollment->group;

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

            if ($priceResult->finalAmount <= 0) {
                $this->advanceEnrollmentBillingDate($enrollment);
                session()->flash('info', __('الاشتراك مجاني - تم التجديد بدون فاتورة'));
                return;
            }

            $invoice = $invoiceService->create([
                'academy_id' => $enrollment->academy_id ?? $program->academy_id,
                'branch_id' => $group?->branch_id ?? $participant->branch_id,
                'billable_type' => $participant->getMorphClass(),
                'billable_id' => $participant->id,
                'total_amount' => $priceResult->finalAmount,
                'subtotal_amount' => $priceResult->baseAmount,
                'discount_amount' => $priceResult->totalDiscount,
                'tax_amount' => 0,
                'due_date' => now()->addDays(7)->toDateString(),
                'contact_name' => $participant->person?->name_ar ?? $participant->person?->name,
                'notes' => 'تجديد اشتراك — ' . $program->name_ar,
            ], [
                [
                    'description' => "تجديد اشتراك: {$program->name_ar}",
                    'quantity' => 1,
                    'unit_price' => $priceResult->baseAmount,
                    'discount_amount' => $priceResult->totalDiscount,
                    'tax_amount' => 0,
                ],
            ], auth()->user());

            // Mark invoice as sent so it appears as payable
            $invoice->update(['status' => InvoiceStatus::Sent]);

            $this->advanceEnrollmentBillingDate($enrollment);

            $enrollment->update([
                'last_billed_at' => now()->toDateString(),
                'payment_status' => 'pending',
            ]);

            $this->selected_invoice_id = $invoice->id;
            $this->payment_amount_display = number_format($invoice->due_amount / 100, 2, '.', '');

        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Throwable $e) {
            Log::error('Renewal invoice generation failed', ['enrollment_id' => $enrollmentId, 'error' => $e->getMessage()]);
            session()->flash('error', __('خطأ في إنشاء فاتورة التجديد'));
        }
    }

    private function generatePendingRenewals(): void
    {
        if (!$this->selected_participant_id) {
            return;
        }

        $enrollments = Enrollment::where('participant_id', $this->selected_participant_id)
            ->where('status', EnrollmentStatus::Active)
            ->whereNotNull('next_billing_date')
            ->where('next_billing_date', '<=', now()->toDateString())
            ->whereHas('program', function ($q) {
                $q->whereIn('renewal_policy', [
                    RenewalPolicy::AutoRenew->value,
                    RenewalPolicy::ManualRenew->value,
                ])->whereNotNull('billing_cycle');
            })
            ->with(['program', 'participant.person', 'group'])
            ->get();

        foreach ($enrollments as $enrollment) {
            $existingUnpaid = Invoice::where('billable_type', Participant::class)
                ->where('billable_id', $this->selected_participant_id)
                ->whereIn('status', [InvoiceStatus::Sent, InvoiceStatus::PartiallyPaid, InvoiceStatus::Overdue, InvoiceStatus::Draft])
                ->where('notes', 'like', '%' . ($enrollment->program?->name_ar ?? 'تجديد') . '%')
                ->exists();

            if (!$existingUnpaid) {
                $this->generateRenewalForEnrollment($enrollment->id);
            }
        }
    }

    private function advanceEnrollmentBillingDate(Enrollment $enrollment): void
    {
        $program = $enrollment->program;
        $current = $enrollment->next_billing_date;

        $next = match ($program->billing_cycle) {
            'monthly' => $current->copy()->addMonth(),
            'quarterly' => $current->copy()->addMonths(3),
            'semi_annual' => $current->copy()->addMonths(6),
            'annual' => $current->copy()->addYear(),
            'per_duration' => $current->copy()->addWeeks($program->program_duration_weeks ?? 4),
            default => $current->copy()->addMonth(),
        };

        if ($program->billing_cycle === 'monthly' && $program->billing_day) {
            $maxDay = $next->daysInMonth;
            $next->day = min($program->billing_day, $maxDay);
        }

        $enrollment->update(['next_billing_date' => $next->toDateString()]);
    }

    public function selectInvoice(int $id): void
    {
        $this->selected_invoice_id = $id;

        // Pre-fill the full balance due
        $invoice = Invoice::find($id);
        if ($invoice) {
            $this->payment_amount_display = number_format($invoice->due_amount / 100, 2, '.', '');
        }
    }

    public function payFullAmount(): void
    {
        $invoice = Invoice::find($this->selected_invoice_id);
        if ($invoice) {
            $this->payment_amount_display = number_format($invoice->due_amount / 100, 2, '.', '');
        }
    }

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

        // Additional validation for step 3: amount cannot exceed balance
        if ($this->currentStep === 3) {
            $invoice = Invoice::find($this->selected_invoice_id);
            $amountPiasters = (int) round((float) $this->payment_amount_display * 100);

            if ($invoice && $amountPiasters > $invoice->due_amount) {
                $this->addError('payment_amount_display', 'المبلغ أكبر من الرصيد المستحق');
                return;
            }
        }

        $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(PaymentService $service): void
    {
        try {
            $amountPiasters = (int) round((float) $this->payment_amount_display * 100);

            // PaymentService::recordPayment() handles:
            // - Creating payment record
            // - Double-entry transactions (debit cash/card, credit AR)
            // - Updating invoice.paid_amount and due_amount
            // - Transitioning invoice status if fully paid
            // - Updating cash session if cash payment

            $payment = $service->recordPayment([
                'invoice_id' => $this->selected_invoice_id,
                'branch_id' => $this->branchId,
                'amount' => $amountPiasters,
                'method' => $this->payment_method,
                'direction' => 'inbound',
                'currency' => 'EGP',
                'payment_date' => now()->toDateString(),
                'notes' => $this->payment_notes ?: null,
            ], auth()->user());

            $this->completed = true;
            $this->receipt_number = $payment->reference ?? null;
            $this->last_payment_uuid = $payment->uuid;
            $this->paid_amount = $amountPiasters;
            $this->currentStep = 5;

            // Store UUIDs for navigation links
            $participant = \App\Domain\Participant\Models\Participant::find($this->selected_participant_id);
            $this->participant_uuid = $participant?->uuid;
            $invoice = \App\Domain\Financial\Models\Invoice::find($this->selected_invoice_id);
            $this->invoice_uuid = $invoice?->uuid;

            session()->flash('success', __('تم تسجيل الدفع بنجاح'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        $searchResults = collect();
        if (strlen($this->search) >= 2) {
            $searchResults = Participant::query()
                ->with('person')
                ->where('branch_id', $this->branchId)
                ->where('status', 'active')
                ->where(function ($q) {
                    $search = $this->search;
                    $q->where('participant_number', 'ilike', "%{$search}%")
                      ->orWhereHas('person', function ($pq) use ($search) {
                          $pq->where('name_ar', 'ilike', "%{$search}%")
                             ->orWhere('name', 'ilike', "%{$search}%")
                             ->orWhere('phone', 'like', "%{$search}%")
                             ->orWhere('national_id', 'like', "%{$search}%");
                      });
                })
                ->limit(10)
                ->get();
        }

        // Outstanding invoices for selected participant
        $invoices = collect();
        $upcomingPayments = collect();
        if ($this->selected_participant_id) {
            $invoices = Invoice::where('billable_type', Participant::class)
                ->where('billable_id', $this->selected_participant_id)
                ->whereIn('status', [
                    InvoiceStatus::Sent,
                    InvoiceStatus::PartiallyPaid,
                    InvoiceStatus::Overdue,
                ])
                ->where('due_amount', '>', 0)
                ->orderBy('due_date')
                ->get();

            // Upcoming installments (from payment plans linked to this participant's invoices)
            $participantInvoiceIds = Invoice::where('billable_type', Participant::class)
                ->where('billable_id', $this->selected_participant_id)
                ->pluck('id');

            $nextInstallments = Installment::whereHas('paymentPlan', fn ($q) => $q->whereIn('invoice_id', $participantInvoiceIds))
                ->where('status', 'pending')
                ->orderBy('due_date')
                ->limit(3)
                ->get();

            // Upcoming enrollment renewals (next_billing_date)
            $upcomingRenewals = Enrollment::where('participant_id', $this->selected_participant_id)
                ->where('status', 'active')
                ->whereNotNull('next_billing_date')
                ->with('program')
                ->orderBy('next_billing_date')
                ->get();

            // Merge into a unified upcoming list
            foreach ($nextInstallments as $inst) {
                $upcomingPayments->push([
                    'type' => 'installment',
                    'label' => 'قسط #' . $inst->sequence,
                    'amount' => $inst->amount,
                    'due_date' => $inst->due_date, // Carbon from model cast
                ]);
            }
            foreach ($upcomingRenewals as $enr) {
                $upcomingPayments->push([
                    'type' => 'renewal',
                    'label' => 'تجديد: ' . ($enr->program?->name_ar ?? '-'),
                    'amount' => null,
                    'due_date' => $enr->next_billing_date,
                    'enrollment_id' => $enr->id,
                ]);
            }
            $upcomingPayments = $upcomingPayments->sortBy(fn ($item) => $item['due_date']->timestamp)->values();
        }

        $selectedInvoice = $this->selected_invoice_id
            ? Invoice::find($this->selected_invoice_id)
            : null;

        return view('livewire.receptionist.collect-payment-wizard', [
            'searchResults' => $searchResults,
            'invoices' => $invoices,
            'upcomingPayments' => $upcomingPayments,
            'selectedInvoice' => $selectedInvoice,
        ]);
    }
}
