<?php

namespace App\Http\Controllers;

use App\Models\FinancialAccount;
use App\Models\Loan;
use App\Models\LoanPayment;
use App\Models\Scopes\BranchScope;
use App\Rules\SameBranchAs;
use App\Services\AuditService;
use App\Services\BranchAccessService;
use App\Services\LoanPaymentService;
use Carbon\CarbonImmutable;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class LoanPaymentController extends Controller
{
    public function __construct(private BranchAccessService $branchAccess)
    {
    }

    public function index(Request $request): View
    {
        $branchIds=$this->allowedBranchIds($request); $search=trim((string)$request->string('search'));
        $loans=collect();
        if($search!==''){
            $loans=Loan::query()->with(['customer','branch','product'])->whereIn('branch_id',$branchIds)->where('status','active')
                ->where(function($query) use ($search): void {
                    $query->where('loan_number','like',"%{$search}%")
                        ->orWhereHas('customer',fn($customer)=>$customer->where('customer_number','like',"%{$search}%")->orWhere('phone','like',"%{$search}%")->orWhere('first_name','like',"%{$search}%")->orWhere('middle_name','like',"%{$search}%")->orWhere('last_name','like',"%{$search}%"));
                })->orderBy('loan_number')->limit(30)->get();
        }
        $recent=LoanPayment::query()->with(['loan.customer','branch','financialAccount','receiver'])->whereIn('branch_id',$branchIds)->latest('payment_date')->latest('id')->paginate(20)->withQueryString();
        return view('payments.index',compact('loans','recent','search'));
    }

    public function create(Request $request, Loan $loan): View
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $loan->load(['customer','branch.business','product','installments.excuse.user','payments']);
        abort_unless($loan->status==='active',422,'Only active loans can receive office payments.');
        $accounts=FinancialAccount::query()->where('branch_id',$loan->branch_id)->where('status','active')->orderBy('name')->get();
        return view('payments.create',compact('loan','accounts'));
    }

    public function store(Request $request, Loan $loan, LoanPaymentService $service): RedirectResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $this->assertSubmittedAccountAccess($request,'financial_account_id');
        $data=$request->validate([
            'amount'=>['required','decimal:0,2','min:0.01'],
            'date'=>['required','date'],
            'method'=>['required',Rule::in(['cash','mobile_money','bank','transfer','other'])],
            'financial_account_id'=>[
                'required','integer','exists:financial_accounts,id',
                new SameBranchAs('financial_accounts',(int)$loan->branch_id,'The receiving account must belong to the same branch as the loan.'),
            ],
            'reference'=>['nullable','string','max:150'],
            'notes'=>['nullable','string','max:2000'],
        ]);
        $account=FinancialAccount::withoutGlobalScope(BranchScope::class)->findOrFail($data['financial_account_id']);
        $payment=$service->post($loan,$account,(string)$data['amount'],CarbonImmutable::parse($data['date']),$data['method'],$data['reference'] ?? null,$data['notes'] ?? null,$request->user());
        AuditService::record('loan.payment_received',$payment,[],[
            'loan_id'=>$payment->loan_id,'loan_number'=>$payment->loan->loan_number,'receipt_number'=>$payment->receipt_number,'amount'=>(string)$payment->amount,
            'principal'=>(string)$payment->principal_amount,'interest'=>(string)$payment->interest_amount,'fees'=>(string)$payment->fees_amount,'penalties'=>(string)$payment->penalties_amount,
            'account_id'=>$payment->financial_account_id,'method'=>$payment->method,'payment_date'=>$payment->payment_date,
        ],$payment->branch_id);
        return redirect()->route('loan-payments.receipt',$payment)->with('success','Loan payment posted successfully.');
    }

    public function receipt(Request $request, LoanPayment $payment): View
    {
        $this->assertBranchAccess($request,$payment->branch_id); $payment->load(['loan.customer','loan.product','branch.business','financialAccount','receiver','allocations.installment']);
        return view('payments.receipt',compact('payment'));
    }

    public function statement(Request $request, Loan $loan): View
    {
        $this->assertBranchAccess($request,$loan->branch_id); $loan->load(['customer','branch.business','product','installments','payments.receiver','statementEntries']);
        return view('payments.statement',compact('loan'));
    }

    private function assertSubmittedAccountAccess(Request $request, string $field): void
    {
        $accountId=$request->integer($field);
        if($accountId<=0) return;
        $account=FinancialAccount::withoutGlobalScope(BranchScope::class)->find($accountId);
        if($account) $this->assertBranchAccess($request,$account->branch_id);
    }

    /** @return array<int> */
    private function allowedBranchIds(Request $request): array
    {
        return $this->branchAccess->accessibleBranchIds($request->user());
    }

    private function assertBranchAccess(Request $request, int $branchId): void
    {
        $this->branchAccess->assertBranchAccessible($request->user(), $branchId);
    }
}
