<?php

namespace App\Livewire\Financial;

use App\Domain\Financial\Models\PaymentProof;
use App\Domain\Financial\Services\PaymentProofService;
use App\Domain\Shared\Context\BranchContext;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Exceptions\InvalidStatusTransitionException;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

/**
 * The staff side of InstaPay, built before the member side on purpose: a proof
 * that can be submitted and not reviewed is a promise to a member that nobody
 * is keeping.
 *
 * The reviewer types the amount from the academy's statement rather than
 * accepting what the payer claimed. That single field is the control — the
 * screenshot is a convenience, and treating it as evidence is what makes this
 * kind of flow forgeable.
 */
#[Layout('layouts.app')]
#[Title('إثباتات التحويل')]
class PaymentProofQueue extends Component
{
    use WithPagination;

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

    #[Locked]
    public ?int $reviewingId = null;

    public string $verifiedAmount = '';

    public string $reviewNotes = '';

    public string $rejectionReason = '';

    public function mount(): void
    {
        $this->authorize('payments.approve_proof');
    }

    public function updatedStatus(): void
    {
        if (! in_array($this->status, PaymentProof::STATUSES, true)) {
            $this->status = 'pending';
        }

        $this->resetPage();
    }

    public function startReview(int $proofId): void
    {
        $this->authorize('payments.approve_proof');

        $proof = $this->findInScope($proofId);

        $this->reviewingId = $proof->id;
        // Pre-filled from the claim only as a starting point; the reviewer is
        // expected to correct it against the statement.
        $this->verifiedAmount = number_format($proof->amount_claimed / 100, 2, '.', '');
        $this->reviewNotes = '';
        $this->rejectionReason = '';
    }

    public function cancelReview(): void
    {
        $this->reviewingId = null;
    }

    public function approve(PaymentProofService $proofs): void
    {
        $this->authorize('payments.approve_proof');

        $this->validate([
            'verifiedAmount' => ['required', 'numeric', 'min:0.01'],
            'reviewNotes' => ['nullable', 'string', 'max:1000'],
        ], [
            'verifiedAmount.required' => __('أدخل المبلغ كما يظهر في كشف الحساب'),
            'verifiedAmount.min' => __('المبلغ يجب أن يكون أكبر من صفر'),
        ]);

        $proof = $this->findInScope($this->reviewingId);

        try {
            $payment = $proofs->approve(
                $proof,
                (int) round(((float) $this->verifiedAmount) * 100),
                auth()->user(),
                $this->reviewNotes ?: null,
                request()->ip(),
            );

            session()->flash('success', __('تم اعتماد التحويل وتسجيل الدفعة') . ' ' . $payment->reference);
        } catch (InvalidStatusTransitionException|DomainException $e) {
            $this->addError('verifiedAmount', $e->getMessage());

            return;
        }

        $this->reviewingId = null;
    }

    public function reject(PaymentProofService $proofs): void
    {
        $this->authorize('payments.approve_proof');

        $this->validate([
            'rejectionReason' => ['required', 'string', 'max:40'],
            'reviewNotes' => ['nullable', 'string', 'max:1000'],
        ], [
            'rejectionReason.required' => __('اذكر سبب الرفض — سيراه') . ' ' . term('member_definite'),
        ]);

        $proof = $this->findInScope($this->reviewingId);

        try {
            $proofs->reject($proof, $this->rejectionReason, auth()->user(), $this->reviewNotes ?: null, request()->ip());
            session()->flash('success', __('تم رفض إثبات التحويل'));
        } catch (InvalidStatusTransitionException $e) {
            $this->addError('rejectionReason', $e->getMessage());

            return;
        }

        $this->reviewingId = null;
    }

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

        $query = PaymentProof::where('status', $this->status)
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->with(['invoice', 'participant.person', 'submitter', 'reviewer'])
            ->orderBy('created_at');

        return view('livewire.financial.payment-proof-queue', [
            'proofs' => $query->paginate(15),
            'pendingCount' => PaymentProof::whereIn('status', ['pending', 'under_review'])
                ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
                ->count(),
            'reviewing' => $this->reviewingId ? PaymentProof::with(['invoice.items', 'participant.person'])->find($this->reviewingId) : null,
            'threshold' => PaymentProofService::MAKER_CHECKER_THRESHOLD,
        ]);
    }

    /**
     * The tenant scope is a global scope, and the branch filter is applied
     * again here rather than trusted from the list query — the id arrives from
     * the browser.
     */
    private function findInScope(?int $proofId): PaymentProof
    {
        abort_if($proofId === null, 400);

        $branchId = app(BranchContext::class)->branchId();

        return PaymentProof::where('id', $proofId)
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->firstOrFail();
    }
}
