<?php

namespace App\Livewire\Financial;

use App\Domain\Financial\Models\Payment;
use App\Domain\Financial\Models\PaymentProof;
use App\Domain\Shared\Context\BranchContext;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;

/**
 * What the system says arrived by transfer, per branch per day, against what
 * the academy's own statement says.
 *
 * A screenshot is not evidence — reconciliation is. Approving a proof records
 * that a member of staff believed a transfer happened; this is the screen that
 * catches the day when the belief and the bank disagree, which is the only way
 * an approval process for money is honest.
 *
 * Three things it surfaces that nothing else does:
 *
 *  - **Ageing.** A proof sitting unreviewed for days is a member who was told
 *    "we will check" and heard nothing.
 *  - **Unmatched approvals.** A proof approved but never posted — impossible
 *    given the CHECK constraint, which is exactly why an empty column here is
 *    a meaningful assertion rather than a hope.
 *  - **Transfers with no proof at all.** A transfer payment recorded at the
 *    till or in the office, with nothing to match against. Not wrong, but the
 *    reconciler needs to know it will not find one.
 */
#[Layout('layouts.app')]
#[Title('مطابقة التحويلات')]
class TransferReconciliation extends Component
{
    #[Url(as: 'from')]
    public string $from = '';

    #[Url(as: 'to')]
    public string $to = '';

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

        $this->from = $this->from ?: now()->startOfMonth()->toDateString();
        $this->to = $this->to ?: now()->toDateString();
    }

    public function render()
    {
        $branchId = app(BranchContext::class)->branchId();

        // One row per branch per day, so a reconciler can sit with a statement
        // and tick along.
        $daily = Payment::query()
            ->whereIn('method', ['instapay', 'bank_transfer'])
            ->where('direction', 'inbound')
            ->whereBetween('payment_date', [$this->from, $this->to])
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->join('branches', 'branches.id', '=', 'payments.branch_id')
            ->groupBy('payments.payment_date', 'payments.branch_id', 'branches.name_ar', 'branches.name')
            ->orderByDesc('payments.payment_date')
            ->select([
                'payments.payment_date',
                'payments.branch_id',
                DB::raw('COALESCE(branches.name_ar, branches.name) as branch_name'),
                DB::raw('COUNT(*) as transfers'),
                DB::raw('SUM(payments.amount) as total'),
            ])
            ->get();

        $proofs = PaymentProof::query()
            ->whereBetween('created_at', [$this->from . ' 00:00:00', $this->to . ' 23:59:59'])
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId));

        return view('livewire.financial.transfer-reconciliation', [
            'daily' => $daily,
            'grandTotal' => (int) $daily->sum('total'),

            // Ageing: how long the oldest unreviewed proof has been waiting.
            'ageing' => (clone $proofs)->whereIn('status', ['pending', 'under_review'])
                ->orderBy('created_at')
                ->with(['participant.person', 'invoice'])
                ->get(),

            'rejected' => (clone $proofs)->where('status', 'rejected')->count(),
            'approved' => (clone $proofs)->where('status', 'approved')->count(),

            // Must always be empty: payment_proofs_approved_payment_check makes
            // an approved proof without a payment unrepresentable. Shown anyway,
            // because a constraint you never look at is a constraint you find
            // out about the hard way.
            'unmatched' => (clone $proofs)->where('status', 'approved')->whereNull('payment_id')->count(),

            // Transfers recorded with no proof behind them — office and till
            // transfers. Not wrong; the reconciler needs to expect them.
            'withoutProof' => Payment::query()
                ->whereIn('method', ['instapay', 'bank_transfer'])
                ->where('direction', 'inbound')
                ->whereBetween('payment_date', [$this->from, $this->to])
                ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
                ->whereNotExists(function ($q) {
                    $q->select(DB::raw(1))
                        ->from('payment_proofs')
                        ->whereColumn('payment_proofs.payment_id', 'payments.id');
                })
                ->count(),
        ]);
    }
}
