<?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\Concerns\ManagesDiscounts;
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 App\Domain\Training\Support\BillingCycle;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

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

    /** Enrollment whose renewal price is currently being composed. */
    public ?int $pricing_enrollment_id = null;

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

    // Step 1: Search
    public string $search = '';
    public string $searchMode = 'participant'; // 'participant' or 'invoice'
    public ?int $selected_participant_id = null;
    public ?string $selected_participant_name = null;
    public bool $selected_participant_is_free = false;

    // 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 = '';

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

    // 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');

        // A payment desk stands in one branch. Refuse the screen outright in
        // all-branches mode rather than letting the operator take money and
        // discover afterwards which ledger it landed in. The id itself is
        // deliberately NOT kept on the component: a public property holding a
        // branch is settable from the browser, and this one would decide which
        // branch the money is booked into.
        $this->getActiveBranchIdOrFail();

        $participantUuid = $participant ?? request()->query('participant');

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

    public function rules(): array
    {
        $branchId = $this->getActiveBranchIdOrFail();

        // `exists:` runs a raw table query, so no Eloquent global scope reaches
        // it. Both ids arrive from public properties the browser can set, so
        // without the branch predicate a foreign id passes validation and then
        // decides which invoice the money is posted against.
        $participantExists = Rule::exists('participants', 'id')->where('branch_id', $branchId);
        $invoiceExists = Rule::exists('invoices', 'id')->where('branch_id', $branchId);

        return match ($this->currentStep) {
            1 => $this->searchMode === 'participant'
                ? ['selected_participant_id' => ['required', $participantExists]]
                : ['selected_invoice_id' => ['required', $invoiceExists]],
            2 => [
                'selected_invoice_id' => ['required', $invoiceExists],
            ],
            3 => [
                'payment_amount_display' => 'required|numeric|min:0.01',
                'payment_method' => 'required|in:cash,card,bank_transfer,wallet,online,cheque,other',
                '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 switchSearchMode(string $mode): void
    {
        $this->searchMode = $mode;
        $this->search = '';
        $this->selected_participant_id = null;
        $this->selected_participant_name = null;
        $this->selected_invoice_id = null;
    }

    public function selectInvoiceDirectly(int $id): void
    {
        $invoice = Invoice::find($id);
        if (!$invoice || $invoice->due_amount <= 0) {
            return;
        }

        if ($invoice->billable_type === Participant::class && $invoice->billable_id) {
            $participant = Participant::with('person')->find($invoice->billable_id);
            $this->selected_participant_id = $participant?->id;
            $this->selected_participant_name = $participant?->person?->name_ar ?? $invoice->contact_name ?? '';
            $this->selected_participant_is_free = (bool) ($participant?->is_free ?? false);
        } else {
            $this->selected_participant_id = null;
            $this->selected_participant_name = $invoice->contact_name ?? __('عميل عابر');
            $this->selected_participant_is_free = false;
        }

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

    public function selectParticipant(int $id, string $name): void
    {
        // The id and the name both arrive from the browser. Resolve the row
        // rather than trusting either: Participant carries BranchScope, so a
        // player from another branch simply does not come back, and the name
        // put on screen is then the one on file.
        $participant = Participant::with('person')->find($id);

        if (! $participant) {
            return;
        }

        $this->selected_participant_id = $participant->id;
        $this->selected_participant_name = $participant->person?->name_ar ?? $participant->person?->name ?? $name;
        $this->selected_participant_is_free = (bool) $participant->is_free;
        $this->generatePendingRenewals();
    }

    /**
     * Show what this participant may have before the invoice is written, so the
     * receptionist can change it rather than discovering it on the receipt.
     */
    public function prepareRenewalPricing(int $enrollmentId): void
    {
        $enrollment = Enrollment::with(['program', 'participant.person', 'group'])->find($enrollmentId);

        // program and participant are both branch-scoped now, so either can come
        // back null on an enrolment whose references were stamped to another
        // branch. There is nothing to price then — and dereferencing them blind
        // would 500 the desk.
        if (!$enrollment
            || $enrollment->participant_id !== $this->selected_participant_id
            || !$enrollment->program
            || !$enrollment->participant) {
            return;
        }

        $this->pricing_enrollment_id = $enrollmentId;
        $this->selectedDiscountIds = [];
        $this->declinedDiscountIds = [];
        $this->clearManualDiscount();

        $this->loadDiscounts(
            priceable: $enrollment->program,
            participant: $enrollment->participant,
            branchId: $enrollment->group?->branch_id ?? $enrollment->participant->branch_id,
            extraContext: ['program_starts_on' => $enrollment->program->program_start_date ?? null],
        );
    }

    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;
        }

        // Same branch-scoped references as prepareRenewalPricing(): say so
        // rather than fataling half way through building the invoice.
        if (!$enrollment->program || !$enrollment->participant) {
            session()->flash('error', __('لا يمكن تجديد هذا الاشتراك من هذا الفرع'));

            return;
        }

        // A manual discount must clear the actor's ceiling before it reaches an invoice.
        if ($this->manualDiscountAmount !== '' && !$this->validateManualDiscount()) {
            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,
            );

            // The picker is authoritative once the operator has touched it: the
            // engine proposes, the receptionist disposes, the invoice records both.
            if ($this->pricing_enrollment_id === $enrollmentId && $this->discountCandidates) {
                $chosenDiscount = $this->selectedDiscountTotal();
                $finalAmount = max(0, $priceResult->baseAmount - $chosenDiscount);
                $discountLines = $this->discountSnapshot();
            } else {
                $chosenDiscount = $priceResult->totalDiscount;
                $finalAmount = $priceResult->finalAmount;
                $discountLines = $priceResult->appliedRules;
            }

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

            // The cycle being settled is the one the enrolment still owes, not
            // "today". Raising September's renewal on the 20th must still date
            // it 1 September, or the money is reported against the wrong month.
            $cycleStart = BillingCycle::startOf($enrollment->next_billing_date ?? now());
            $monthKey = BillingCycle::monthKey($cycleStart);
            $monthLabel = BillingCycle::monthLabel($cycleStart);

            $invoice = DB::transaction(function () use (
                $invoiceService, $enrollment, $program, $participant, $group,
                $priceResult, $finalAmount, $chosenDiscount, $discountLines,
                $cycleStart, $monthKey, $monthLabel
            ) {
                $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' => $finalAmount,
                    'subtotal_amount' => $priceResult->baseAmount,
                    // Header only. recalculateTotals() computes
                    // total = sum(line totals) - header discount, so repeating
                    // the figure on the line subtracts it twice.
                    'discount_amount' => $chosenDiscount,
                    'tax_amount' => 0,
                    'issue_date' => $cycleStart->toDateString(),
                    'due_date' => $cycleStart->copy()->addDays(7)->toDateString(),
                    'contact_name' => $participant->person?->name_ar ?? $participant->person?->name,
                    'notes' => "تجديد اشتراك {$monthLabel} — {$program->name_ar}",
                    'metadata' => [
                        'month' => $monthKey,
                        'renewal' => true,
                        'renewal_enrollment_id' => (string) $enrollment->id,
                    ],
                ], [
                    [
                        'description' => "اشتراك {$monthLabel}: {$program->name_ar}",
                        'quantity' => 1,
                        'unit_price' => $priceResult->baseAmount,
                        'discount_amount' => 0,
                        'tax_amount' => 0,
                    ],
                ], auth()->user());

                // Freeze the discount names on the invoice so the receipt stays
                // readable after the rules change.
                $invoice->update([
                    'status' => InvoiceStatus::Sent,
                    'metadata' => array_merge($invoice->metadata ?? [], [
                        'applied_discounts' => $discountLines,
                    ]),
                ]);

                $this->advanceEnrollmentBillingDate($enrollment, $cycleStart);

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

                return $invoice;
            });

            $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) {
            // Was this exact cycle already raised — by the nightly command, or
            // by this screen a minute ago?
            //
            // The old guard asked a different question and got it wrong twice.
            // It matched `notes LIKE '%{programme name}%'`, and OC-Sport has
            // three programmes called "فريق 2018", so one player's renewal
            // suppressed another's. And it only looked at UNPAID statuses, so
            // the moment a renewal was paid the guard stopped seeing it and the
            // next visit to this screen billed the same month again.
            $monthKey = BillingCycle::monthKey($enrollment->next_billing_date ?? now());

            $alreadyRaised = Invoice::where('billable_type', Participant::class)
                ->where('billable_id', $this->selected_participant_id)
                ->where('status', '!=', InvoiceStatus::Cancelled->value)
                ->where('metadata->month', $monthKey)
                ->where('metadata->renewal_enrollment_id', (string) $enrollment->id)
                ->exists();

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

    /**
     * Hand over to the cycle after the one just settled.
     *
     * Advancing from the CYCLE rather than from `now()` is what stops a late
     * renewal eating a month: settling August's cycle on 20 September moves the
     * anchor to 1 September, so September is still owed and still gets billed.
     * Advancing from `now()` would have jumped straight to 1 October.
     */
    private function advanceEnrollmentBillingDate(Enrollment $enrollment, ?\Illuminate\Support\Carbon $cycleStart = null): void
    {
        $cycleStart ??= BillingCycle::startOf($enrollment->next_billing_date ?? now());

        $next = BillingCycle::next(
            $cycleStart,
            $enrollment->program?->billing_cycle,
            $enrollment->program?->program_duration_weeks
        );

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

    public function updatedPriceOverrideEnabled(): void
    {
        if (!$this->priceOverrideEnabled) {
            $this->priceOverrideInput = '';
            $this->priceOverrideReason = '';
        }
    }

    public function applyAdminOverride(): void
    {
        $actor = auth()->user();
        if (!$actor->is_super_admin) {
            return;
        }

        $invoice = Invoice::find($this->selected_invoice_id);
        if (!$invoice || $this->priceOverrideInput === '') {
            return;
        }

        $newTotalPiasters = (int) round((float) $this->priceOverrideInput * 100);
        if ($newTotalPiasters < 0) {
            $this->addError('priceOverrideInput', __('المبلغ لا يمكن أن يكون سالبًا'));
            return;
        }

        $originalTotal = $invoice->total_amount;
        $originalDue = $invoice->due_amount;
        $originalDiscount = $invoice->discount_amount;

        // The override sets a new total — the difference becomes admin discount
        $adminDiscount = $originalTotal + $originalDiscount - $newTotalPiasters;
        $newDueAmount = max(0, $newTotalPiasters - $invoice->paid_amount);

        // Store audit trail in metadata
        $metadata = $invoice->metadata ?? [];
        $metadata['admin_override'] = [
            'applied_at' => now()->toIso8601String(),
            'applied_by_id' => $actor->id,
            'applied_by_name' => $actor->name,
            'original_total_piasters' => $originalTotal,
            'original_due_piasters' => $originalDue,
            'new_total_piasters' => $newTotalPiasters,
            'discount_piasters' => $adminDiscount,
            'reason' => $this->priceOverrideReason ?: null,
            'participant_id' => $this->selected_participant_id,
            'participant_name' => $this->selected_participant_name,
        ];

        $invoice->update([
            'discount_amount' => $adminDiscount,
            'total_amount' => $newTotalPiasters,
            'due_amount' => $newDueAmount,
            'metadata' => $metadata,
        ]);

        // Audit log
        Log::channel('audit')->info('admin_invoice_override', [
            'actor_id' => $actor->id,
            'actor_name' => $actor->name,
            'invoice_id' => $invoice->id,
            'invoice_number' => $invoice->number,
            'participant_name' => $this->selected_participant_name,
            'original_total' => $originalTotal,
            'new_total' => $newTotalPiasters,
            'reason' => $this->priceOverrideReason,
        ]);

        // Refresh payment amount display
        $this->payment_amount_display = number_format($newDueAmount / 100, 2, '.', '');

        // Reset override UI
        $this->priceOverrideEnabled = false;
        $this->priceOverrideInput = '';
        $this->priceOverrideReason = '';

        session()->flash('success', __('تم تعديل مبلغ الفاتورة بنجاح'));
    }

    public function updatedSelectedInvoiceId(): void
    {
        if ($this->selected_invoice_id) {
            $invoice = Invoice::find($this->selected_invoice_id);
            if ($invoice) {
                $this->payment_amount_display = number_format($invoice->due_amount / 100, 2, '.', '');
            }
        }
        // Reset override when changing invoice
        $this->priceOverrideEnabled = false;
        $this->priceOverrideInput = '';
        $this->priceOverrideReason = '';
    }

    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 {
            // confirm() is browser-callable and $selected_invoice_id is a public
            // property, so the id is re-resolved here instead of being trusted
            // from the step that validated it. Invoice carries BranchScope — an
            // id belonging to another branch resolves to nothing and no money
            // moves.
            $invoice = Invoice::find($this->selected_invoice_id);

            if (! $invoice) {
                session()->flash('error', __('الفاتورة المختارة غير موجودة'));

                return;
            }

            $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' => $invoice->id,
                'branch_id' => $this->getActiveBranchIdOrFail(),
                '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 = $this->selected_participant_id
                ? Participant::find($this->selected_participant_id)
                : null;
            $this->participant_uuid = $participant?->uuid;
            $this->invoice_uuid = $invoice->uuid;

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

    public function render()
    {
        $searchResults = collect();
        $invoiceSearchResults = collect();

        if (strlen($this->search) >= 2) {
            if ($this->searchMode === 'participant') {
                $searchResults = Participant::query()
                    ->with('person')
                    ->whereIn('status', ['active', 'registered', 'frozen', 'inactive'])
                    ->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();
            } else {
                $invoiceSearchResults = Invoice::query()
                    ->whereIn('status', [
                        InvoiceStatus::Sent,
                        InvoiceStatus::PartiallyPaid,
                        InvoiceStatus::Overdue,
                        InvoiceStatus::Draft,
                    ])
                    ->where('due_amount', '>', 0)
                    ->where(function ($q) {
                        $search = $this->search;
                        $q->where('number', 'ilike', "%{$search}%")
                          ->orWhere('contact_name', 'ilike', "%{$search}%");
                    })
                    ->orderBy('due_date')
                    ->limit(10)
                    ->get();
            }
        }

        // Outstanding invoices for the selected participant. This used to say
        // "all branches — POS can sell cross-branch" and meant it; branch is now
        // an isolation boundary, and BranchScope on Invoice narrows this to the
        // desk's own branch without a predicate here.
        $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,
                    InvoiceStatus::Draft,
                ])
                ->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,
            'invoiceSearchResults' => $invoiceSearchResults,
            'invoices' => $invoices,
            'upcomingPayments' => $upcomingPayments,
            'selectedInvoice' => $selectedInvoice,
            'isSuperAdmin' => auth()->user()?->is_super_admin ?? false,
            // The picker must say which branch it is showing — a receptionist who
            // cannot see why a discount is missing will phone the office. Read
            // from the context, never from a property the browser could set.
            'branchName' => $this->branchContext()->currentBranch()?->name_ar,
        ]);
    }
}
