<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\FinancialAccount;
use App\Models\Loan;
use App\Models\RemotePaymentSubmission;
use App\Rules\SameBranchAs;
use App\Services\AuditService;
use App\Services\RemotePaymentService;
use App\Services\SecureFileService;
use Carbon\CarbonImmutable;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class RemotePaymentController extends Controller
{
    public function index(Request $request): View
    {
        $branchIds=$this->allowedBranchIds($request);
        $status=(string)$request->string('status');
        if(!in_array($status,RemotePaymentSubmission::STATUSES,true)) $status='';
        $search=trim((string)$request->string('search'));

        $query=RemotePaymentSubmission::query()->with(['loan.customer','branch','submitter','verifier','loanPayment'])
            ->whereIn('branch_id',$branchIds);
        if($status!=='') $query->where('status',$status);
        if($search!=='') {
            $query->where(function($q) use ($search): void {
                $q->where('submission_number','like',"%{$search}%")
                    ->orWhere('transaction_reference','like',"%{$search}%")
                    ->orWhereHas('loan',fn($loan)=>$loan->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('last_name','like',"%{$search}%"));
            });
        }
        $submissions=$query->latest('submitted_at')->latest('id')->paginate(20)->withQueryString();
        return view('remote-payments.index',compact('submissions','status','search'));
    }

    public function create(): View
    {
        return view('remote-payments.create');
    }

    public function store(Request $request, RemotePaymentService $service): RedirectResponse
    {
        $data=$request->validate([
            'loan_number'=>['required','string','max:100'],
            'amount'=>['required','decimal:0,2','min:0.01'],
            'payment_channel'=>['required',Rule::in(RemotePaymentSubmission::CHANNELS)],
            'transaction_reference'=>['required','string','max:150'],
            'proof'=>['required','file','mimes:pdf,jpg,jpeg,png,webp','max:10240'],
            'notes'=>['nullable','string','max:2000'],
        ]);
        $branchIds=$this->allowedBranchIds($request);
        $loan=Loan::query()->with(['customer','branch'])->whereIn('branch_id',$branchIds)->where('status','active')->where('loan_number',trim($data['loan_number']))->first();
        if(!$loan) throw ValidationException::withMessages(['loan_number'=>'No active loan in your accessible branches matches this loan number.']);

        $submission=$service->submit($loan,(string)$data['amount'],$data['payment_channel'],$data['transaction_reference'],$data['proof'],$data['notes'] ?? null,'staff',$request->user());
        AuditService::record('remote_payment.submitted',$submission,[],[
            'submission_number'=>$submission->submission_number,'loan_id'=>$submission->loan_id,'amount'=>(string)$submission->amount,
            'channel'=>$submission->payment_channel,'transaction_reference'=>$submission->transaction_reference,'source'=>'staff',
        ],$submission->branch_id);
        return redirect()->route('remote-payments.index')->with('success','Remote payment submitted for verification.');
    }

    public function show(Request $request, RemotePaymentSubmission $submission): View
    {
        $this->assertBranchAccess($request,$submission->branch_id);
        $submission->load(['loan.customer','loan.product','branch.business','submitter','verifier','financialAccount','loanPayment']);
        $accounts=FinancialAccount::query()->where('branch_id',$submission->branch_id)->where('status','active')->orderBy('name')->get();
        return view('remote-payments.show',compact('submission','accounts'));
    }

    public function approve(Request $request, RemotePaymentSubmission $submission, RemotePaymentService $service): RedirectResponse
    {
        $this->assertBranchAccess($request,$submission->branch_id);
        $data=$request->validate([
            'financial_account_id'=>['required','integer','exists:financial_accounts,id'],
            'payment_date'=>['required','date','before_or_equal:today'],
            'verification_notes'=>['nullable','string','max:2000'],
        ]);
        $account=FinancialAccount::findOrFail($data['financial_account_id']);
        $this->assertBranchAccess($request,$account->branch_id);
        $request->validate([
            'financial_account_id'=>[
                new SameBranchAs('financial_accounts',(int)$submission->branch_id,'The receiving account must belong to the remote payment branch.'),
            ],
        ]);
        $approved=$service->approve($submission,$account,CarbonImmutable::parse($data['payment_date']),$data['verification_notes'] ?? null,$request->user());
        AuditService::record('remote_payment.approved',$approved,['status'=>'pending_verification'],[
            'status'=>'approved','submission_number'=>$approved->submission_number,'loan_payment_id'=>$approved->loan_payment_id,
            'account_id'=>$approved->financial_account_id,'amount'=>(string)$approved->amount,
        ],$approved->branch_id);
        return redirect()->route('remote-payments.show',$approved)->with('success','Remote payment approved and posted successfully.');
    }

    public function reject(Request $request, RemotePaymentSubmission $submission, RemotePaymentService $service): RedirectResponse
    {
        $this->assertBranchAccess($request,$submission->branch_id);
        $data=$request->validate(['verification_notes'=>['required','string','max:2000']]);
        $rejected=$service->reject($submission,$data['verification_notes'],$request->user());
        AuditService::record('remote_payment.rejected',$rejected,['status'=>'pending_verification'],[
            'status'=>'rejected','submission_number'=>$rejected->submission_number,'reason'=>$rejected->verification_notes,
        ],$rejected->branch_id);
        return redirect()->route('remote-payments.show',$rejected)->with('success','Remote payment rejected. No financial balances were changed.');
    }

    public function proof(Request $request, RemotePaymentSubmission $submission, SecureFileService $files): StreamedResponse
    {
        $this->assertBranchAccess($request,$submission->branch_id);
        AuditService::record('remote_payment.proof_downloaded',$submission,[],['submission_number'=>$submission->submission_number],$submission->branch_id);
        return $files->download($submission->proof_path,$submission->proof_original_name);
    }

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

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