<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\FinancialAccount;
use App\Models\Loan;
use App\Models\LoanEarlySettlement;
use App\Models\LoanRestructuring;
use App\Models\LoanRestructuringDocument;
use App\Rules\SameBranchAs;
use App\Services\AuditService;
use App\Services\LoanEarlySettlementService;
use App\Services\LoanRestructuringService;
use App\Services\SecureFileService;
use Carbon\CarbonImmutable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class LoanAdjustmentController extends Controller
{
    public function index(Request $request): View
    {
        $branchIds = $this->allowedBranchIds($request);
        $search = trim((string) $request->string('search'));

        $active = Loan::query()->with(['customer','branch','product'])
            ->whereIn('branch_id',$branchIds)->where('status','active')
            ->when($search !== '', fn ($query) => $query->where(function ($sub) use ($search): void {
                $sub->where('loan_number','like',"%{$search}%")
                    ->orWhereHas('customer', fn ($customer) => $customer->where('customer_number','like',"%{$search}%")
                        ->orWhere('first_name','like',"%{$search}%")->orWhere('last_name','like',"%{$search}%")->orWhere('phone','like',"%{$search}%"));
            }))->latest('id')->paginate(15,['*'],'active_page')->withQueryString();

        $restructurings = LoanRestructuring::query()->with(['loan.customer','branch','requester','approver','rejecter'])
            ->whereIn('branch_id',$branchIds)
            ->when($search !== '', fn ($query) => $query->where(function ($sub) use ($search): void {
                $sub->where('restructuring_number','like',"%{$search}%")
                    ->orWhereHas('loan', fn ($loan) => $loan->where('loan_number','like',"%{$search}%")
                        ->orWhereHas('customer', fn ($customer) => $customer->where('customer_number','like',"%{$search}%")
                            ->orWhere('first_name','like',"%{$search}%")->orWhere('last_name','like',"%{$search}%")));
            }))->latest('id')->paginate(15,['*'],'restructuring_page')->withQueryString();

        $settlements = LoanEarlySettlement::query()->with(['loan.customer','branch','financialAccount','settler'])
            ->whereIn('branch_id',$branchIds)
            ->when($search !== '', fn ($query) => $query->where(function ($sub) use ($search): void {
                $sub->where('settlement_number','like',"%{$search}%")
                    ->orWhereHas('loan', fn ($loan) => $loan->where('loan_number','like',"%{$search}%")
                        ->orWhereHas('customer', fn ($customer) => $customer->where('customer_number','like',"%{$search}%")
                            ->orWhere('first_name','like',"%{$search}%")->orWhere('last_name','like',"%{$search}%")));
            }))->latest('id')->paginate(15,['*'],'settlement_page')->withQueryString();

        return view('loan-adjustments.index',compact('active','restructurings','settlements'));
    }

    public function settlementForm(Request $request, Loan $loan, LoanEarlySettlementService $service): View
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        abort_unless($loan->status === 'active',409,'Only active loans can be early-settled.');
        $loan->load(['customer','branch.business','product','collaterals.category']);
        $accounts = FinancialAccount::where('branch_id',$loan->branch_id)->where('status','active')->orderBy('name')->get();
        $quote = $service->quote($loan,CarbonImmutable::today(),'0.00',$request->user());
        return view('loan-adjustments.settlement',compact('loan','accounts','quote'));
    }

    public function settlementPreview(Request $request, Loan $loan, LoanEarlySettlementService $service): JsonResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $data = $request->validate([
            'date'=>['required','date'],
            'settlement_adjustment'=>['nullable','regex:/^[+-]?\d+(?:\.\d{1,2})?$/'],
        ]);
        return response()->json($service->quote(
            $loan,CarbonImmutable::parse($data['date']),$data['settlement_adjustment'] ?? '0.00',$request->user()
        ));
    }

    public function settle(Request $request, Loan $loan, LoanEarlySettlementService $service): RedirectResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $data = $request->validate([
            'financial_account_id'=>['required','integer','exists:financial_accounts,id'],
            'date'=>['required','date'],
            'settlement_adjustment'=>['nullable','regex:/^[+-]?\d+(?:\.\d{1,2})?$/'],
            'adjustment_reason'=>['nullable','string','max:2000'],
            'reference'=>['nullable','string','max:150'],
            '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)$loan->branch_id,'The settlement account must belong to the loan branch.'),
            ],
        ]);
        $settlement = $service->execute(
            $loan,$account,CarbonImmutable::parse($data['date']),$data['settlement_adjustment'] ?? '0.00',
            $data['adjustment_reason'] ?? null,$data['reference'] ?? null,$data['notes'] ?? null,$request->user()
        );
        AuditService::record('loan_early_settlement.executed',$settlement,[],[
            'loan_id'=>$settlement->loan_id,'principal'=>(string)$settlement->principal_amount,'interest'=>(string)$settlement->interest_amount,
            'fees'=>(string)$settlement->fees_amount,'penalties'=>(string)$settlement->penalties_amount,
            'settlement_adjustment'=>(string)$settlement->settlement_adjustment,'final_amount'=>(string)$settlement->final_amount,
            'financial_account_id'=>$settlement->financial_account_id,
        ],$settlement->branch_id);
        return redirect()->route('loan-early-settlements.show',$settlement)->with('success','Loan early settlement posted successfully. The loan is fully paid and linked collateral is ready for release.');
    }

    public function settlementShow(Request $request, LoanEarlySettlement $settlement): View
    {
        $this->assertBranchAccess($request,$settlement->branch_id);
        $settlement->load(['loan.customer','loan.product','loan.collaterals.category','branch.business','financialAccount','settler','accountTransaction']);
        return view('loan-adjustments.settlement-show',compact('settlement'));
    }

    public function restructuringForm(Request $request, Loan $loan): View
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        abort_unless($loan->status === 'active',409,'Only active loans can be restructured.');
        $loan->load(['customer','branch.business','product','installments']);
        return view('loan-adjustments.restructure',compact('loan'));
    }

    public function restructuringPreview(Request $request, Loan $loan, LoanRestructuringService $service): JsonResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $data = $request->validate(['new_schedule'=>['required','string','max:30000']]);
        return response()->json($service->preview($loan,$data['new_schedule'],$request->user()));
    }

    public function restructuringStore(Request $request, Loan $loan, LoanRestructuringService $service, SecureFileService $files): RedirectResponse
    {
        $this->assertBranchAccess($request,$loan->branch_id);
        $data = $request->validate([
            'new_schedule'=>['required','string','max:30000'],
            'reason'=>['required','string','max:4000'],
            'documents'=>['nullable','array','max:10'],
            'documents.*'=>['file','mimes:jpg,jpeg,png,webp,pdf,doc,docx','max:8192'],
        ]);
        $record = $service->request($loan,$data['new_schedule'],$data['reason'],$request->user());
        foreach ($request->file('documents',[]) as $file) {
            $path = $files->store($file,'loan-restructurings/'.$record->id);
            LoanRestructuringDocument::create([
                'loan_restructuring_id'=>$record->id,'document_type'=>'Supporting document','original_name'=>$file->getClientOriginalName(),
                'file_path'=>$path,'mime_type'=>$file->getMimeType(),'file_size'=>$file->getSize(),'uploaded_by'=>$request->user()->id,
            ]);
        }
        AuditService::record('loan_restructuring.requested',$record,[],[
            'loan_id'=>$record->loan_id,'previous_schedule_version'=>$record->previous_schedule_version,
            'new_schedule_version'=>$record->new_schedule_version,'total_outstanding'=>(string)$record->total_outstanding,
            'reason'=>$record->reason,'documents_count'=>$record->documents()->count(),
        ],$record->branch_id);
        return redirect()->route('loan-restructurings.show',$record)->with('success','Restructuring request created. The current repayment schedule remains active until approval.');
    }

    public function restructuringShow(Request $request, LoanRestructuring $restructuring): View
    {
        $this->assertBranchAccess($request,$restructuring->branch_id);
        $restructuring->load(['loan.customer','loan.product','branch.business','requester','approver','rejecter','documents.uploader']);
        return view('loan-adjustments.restructuring-show',compact('restructuring'));
    }

    public function restructuringDecision(Request $request, LoanRestructuring $restructuring, LoanRestructuringService $service): RedirectResponse
    {
        $this->assertBranchAccess($request,$restructuring->branch_id);
        $data = $request->validate([
            'decision'=>['required',Rule::in(['approve','reject'])],
            'comments'=>['nullable','required_if:decision,reject','string','max:2000'],
        ]);
        $before = $restructuring->only(['status','approved_by','approved_at','rejected_by','rejected_at']);
        $updated = $data['decision'] === 'approve'
            ? $service->approve($restructuring,$request->user(),$data['comments'] ?? null)
            : $service->reject($restructuring,$request->user(),$data['comments']);
        AuditService::record('loan_restructuring.'.$updated->status,$updated,$before,$updated->only([
            'status','approved_by','approved_at','rejected_by','rejected_at','decision_comments','new_schedule_version',
        ]),$updated->branch_id);
        return back()->with('success',$updated->status === 'approved'?'Restructuring approved. The preserved old schedule is retired and the new schedule is now active.':'Restructuring request rejected. The current schedule was not changed.');
    }

    public function restructuringDocumentDownload(Request $request, LoanRestructuring $restructuring, LoanRestructuringDocument $document, SecureFileService $files): StreamedResponse
    {
        $this->assertBranchAccess($request,$restructuring->branch_id);
        abort_unless($document->loan_restructuring_id === $restructuring->id,404);
        return $files->download($document->file_path,$document->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);
    }
}
