<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\FinancialAccount;
use App\Models\Loan;
use App\Models\LoanDisbursement;
use App\Rules\SameBranchAs;
use App\Services\AuditService;
use App\Services\BranchAccessService;
use App\Services\LoanDisbursementService;
use Carbon\CarbonImmutable;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class LoanDisbursementController extends Controller
{
    public function index(Request $request): View
    {
        $branchIds = $this->allowedBranchIds($request);
        $search = trim((string) $request->string('search'));
        $ready = Loan::query()->with(['customer','branch','product'])->whereIn('branch_id',$branchIds)->where('status','ready_for_disbursement')->whereNull('top_up_source_loan_id')
            ->when($search !== '', fn ($query) => $query->where(function ($sub) use ($search): void {
                $sub->where('loan_number','like',"%{$search}%")->orWhereHas('customer', fn ($customer) => $customer->where('first_name','like',"%{$search}%")->orWhere('middle_name','like',"%{$search}%")->orWhere('last_name','like',"%{$search}%")->orWhere('customer_number','like',"%{$search}%")->orWhere('phone','like',"%{$search}%"));
            }))->latest('approved_at')->paginate(20,['*'],'ready_page')->withQueryString();
        $history = LoanDisbursement::query()->with(['loan.customer','branch','financialAccount','user'])->whereIn('branch_id',$branchIds)
            ->when($search !== '', fn ($query) => $query->where(function ($sub) use ($search): void {
                $sub->where('disbursement_number','like',"%{$search}%")->orWhere('reference','like',"%{$search}%")->orWhereHas('loan',fn ($loan) => $loan->where('loan_number','like',"%{$search}%"));
            }))->latest('id')->paginate(20,['*'],'history_page')->withQueryString();
        return view('disbursements.index',compact('ready','history'));
    }

    public function create(Request $request, Loan $loan): View|RedirectResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        abort_if($loan->top_up_source_loan_id,409,'Top-up replacement loans must be executed from Loan Top-Ups so only the net customer cash is disbursed.');
        if ($loan->disbursement()->exists()) return redirect()->route('loan-disbursements.index')->with('error','This loan has already been disbursed and cannot be posted again.');
        if ($loan->status !== 'ready_for_disbursement') return redirect()->route('loan-disbursements.index')->with('error','Only an undisbursed loan that is Ready for Disbursement can be posted.');
        $loan->load(['customer','branch.business','product','documents']);
        $accounts=FinancialAccount::query()->where('branch_id',$loan->branch_id)->where('status','active')->orderBy('name')->get();
        $cashAccounts=$accounts->where('account_type','cash')->values();
        return view('disbursements.create',compact('loan','accounts','cashAccounts'));
    }

    public function store(Request $request, Loan $loan, LoanDisbursementService $service): RedirectResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $data=$request->validate([
            'amount'=>['required','decimal:0,2','min:0.01'],'financial_account_id'=>['required','integer','exists:financial_accounts,id'],
            'processing_fee_account_id'=>['nullable','integer','exists:financial_accounts,id'],'date'=>['required','date'],
            'reference'=>['nullable','string','max:150'],'notes'=>['nullable','string','max:2000'],
        ]);
        $account=FinancialAccount::withoutGlobalScope(\App\Models\Scopes\BranchScope::class)->findOrFail($data['financial_account_id']); $this->assertBranchAccess($request,$account->branch_id);
        $feeAccount=!empty($data['processing_fee_account_id']) ? FinancialAccount::withoutGlobalScope(\App\Models\Scopes\BranchScope::class)->findOrFail($data['processing_fee_account_id']) : null;
        if($feeAccount) $this->assertBranchAccess($request,$feeAccount->branch_id);
        $request->validate([
            'financial_account_id'=>[new SameBranchAs('financial_accounts',(int)$loan->branch_id,'The disbursement account must belong to the same branch as the loan.')],
            'processing_fee_account_id'=>['nullable',new SameBranchAs('financial_accounts',(int)$loan->branch_id,'The processing fee account must belong to the same branch as the loan.')],
        ]);
        $disbursement=$service->disburse($loan,$account,(string)$data['amount'],CarbonImmutable::parse($data['date']),$data['reference']??null,$data['notes']??null,$request->user(),$feeAccount);
        AuditService::record('loan.disbursed',$disbursement,[],[
            'loan_id'=>$disbursement->loan_id,'loan_number'=>$disbursement->loan->loan_number,'disbursement_number'=>$disbursement->disbursement_number,
            'account_id'=>$disbursement->financial_account_id,'amount'=>(string)$disbursement->amount,'processing_fee'=>(string)$loan->processing_fee,
            'processing_fee_payment_mode'=>$loan->processing_fee_payment_mode,'date'=>$disbursement->disbursement_date->toDateString(),
        ],$disbursement->branch_id);
        return redirect()->route('loan-disbursements.receipt',$disbursement)->with('success','Loan disbursed successfully. Print or save the disbursement receipt.');
    }

    public function receipt(Request $request, LoanDisbursement $disbursement): View
    {
        $this->assertBranchAccess($request,$disbursement->branch_id);
        $disbursement->load(['loan.customer','loan.product','branch.business','financialAccount','user']);
        return view('disbursements.receipt',compact('disbursement'));
    }

    private function allowedBranchIds(Request $request)
    {
        return collect(app(BranchAccessService::class)->accessibleBranchIds($request->user()));
    }

    private function assertBranchAccess(Request $request, int $branchId): void { abort_unless($request->user()->canAccessBranch($branchId),403); }
}
