<?php

namespace App\Domain\Financial\Listeners;

use App\Domain\Financial\Events\PaymentReceived;
use App\Domain\Financial\Models\CashSession;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;

class UpdateCashSessionTotals implements ShouldQueue
{
    public function handle(PaymentReceived $event): void
    {
        try {
            $payment = $event->payment;

            if ($payment->method !== \App\Domain\Financial\Enums\PaymentMethod::Cash) {
                return;
            }

            // The drawer the money actually went into. The payment names it
            // whenever the collecting screen knew it, and that is the only
            // answer that cannot be wrong.
            //
            // Falling back to "this user's open session" has to be narrowed to
            // the payment's own branch: a cashier may now hold a drawer open in
            // more than one branch, and this listener runs on a queue where the
            // branch scope is off, so an unqualified first() would count one
            // branch's cash into another branch's box.
            $cashSession = $payment->cash_session_id
                ? CashSession::withoutBranchScope()
                    ->where('id', $payment->cash_session_id)
                    ->where('status', 'open')
                    ->first()
                : null;

            $cashSession ??= CashSession::forBranch($payment->branch_id ? (int) $payment->branch_id : null)
                ->where('user_id', $event->actor->id)
                ->where('status', 'open')
                ->orderByDesc('opened_at')
                ->first();

            if (!$cashSession) {
                return;
            }

            // A POS sale already counted itself once in POSService, and a split
            // sale raises several PaymentReceived events for that one sale — so
            // only non-POS collections count an operation here. `total_cash_in`
            // is incremented in exactly one place: here. POSService used to
            // increment it as well, which inflated the expected drawer by
            // double on every POS cash sale and produced a phantom variance at
            // close.
            if (($payment->metadata['source'] ?? null) !== 'pos') {
                $cashSession->increment('transactions_count');
            }

            $cashSession->increment('total_cash_in', $payment->amount);
        } catch (\Throwable $e) {
            Log::error('UpdateCashSessionTotals failed: ' . $e->getMessage(), [
                'payment_id' => $event->payment->id,
            ]);
        }
    }

    public function failed(PaymentReceived $event, \Throwable $exception): void
    {
        Log::critical('UpdateCashSessionTotals PERMANENTLY FAILED', [
            'payment_id' => $event->payment->id,
            'error' => $exception->getMessage(),
        ]);
    }
}
