<?php

namespace App\Http\Controllers;

use App\Models\Business;
use App\Models\LoanProduct;
use App\Services\AuditService;
use App\Services\RepaymentScheduleGenerator;
use Carbon\CarbonImmutable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use InvalidArgumentException;

class LoanProductController extends Controller
{
    public function index(Request $request): View
    {
        Gate::authorize('viewAny', LoanProduct::class);

        $query = LoanProduct::with('business')->orderBy('name');
        if ($request->filled('search')) $query->where('name','like','%'.trim((string)$request->string('search')).'%');
        if ($request->filled('status')) $query->where('status',$request->string('status'));
        return view('loan-products.index',['products'=>$query->paginate(25)->withQueryString()]);
    }

    public function create(): View
    {
        Gate::authorize('create', LoanProduct::class);

        return view('loan-products.form',['product'=>new LoanProduct([
            'duration'=>1,'duration_unit'=>'months','interest_rate'=>'0','interest_calculation_method'=>'flat','repayment_frequency'=>'monthly','grace_period_days'=>0,
            'late_payment_penalty_type'=>'none','late_payment_penalty_value'=>'0','processing_fee_type'=>'fixed','processing_fee_value'=>'0','processing_fee_payment_mode'=>'financed',
            'insurance_fee_type'=>'fixed','insurance_fee_value'=>'0','other_charges'=>'0','payment_deadline_enabled'=>false,'payment_deadline_time'=>'17:00','status'=>'active',
        ])]);
    }

    public function store(Request $request): RedirectResponse
    {
        Gate::authorize('create', LoanProduct::class);
        $data=$this->validated($request); $data['business_id']=Business::firstOrFail()->id; $data['created_by']=$request->user()->id; $data['updated_by']=$request->user()->id;
        $product=LoanProduct::create($data); AuditService::record('loan_product.created',$product,[],$product->toArray());
        return redirect()->route('loan-products.show',$product)->with('success','Loan product created.');
    }

    public function show(LoanProduct $loanProduct): View
    {
        Gate::authorize('view', $loanProduct);
        return view('loan-products.show',['product'=>$loanProduct]);
    }

    public function edit(LoanProduct $loanProduct): View
    {
        Gate::authorize('update', $loanProduct);
        return view('loan-products.form',['product'=>$loanProduct]);
    }

    public function update(Request $request, LoanProduct $loanProduct): RedirectResponse
    {
        Gate::authorize('update', $loanProduct);
        $before=$loanProduct->toArray(); $data=$this->validated($request,$loanProduct); $data['updated_by']=$request->user()->id; $loanProduct->update($data);
        AuditService::record('loan_product.updated',$loanProduct,$before,$loanProduct->fresh()->toArray());
        return redirect()->route('loan-products.show',$loanProduct)->with('success','Loan product updated.');
    }

    public function preview(Request $request, LoanProduct $loanProduct, RepaymentScheduleGenerator $generator): JsonResponse
    {
        Gate::authorize('preview', $loanProduct);
        $data=$request->validate(['principal'=>['required','numeric','min:0.01'],'start_date'=>['required','date'],'custom_dates'=>['nullable','array'],'custom_dates.*'=>['date']]);
        try { $result=$generator->generate($loanProduct,(string)$data['principal'],CarbonImmutable::parse($data['start_date']),$data['custom_dates']??[]); }
        catch (InvalidArgumentException $e) { return response()->json(['message'=>$e->getMessage()],422); }
        return response()->json($result);
    }

    private function validated(Request $request, ?LoanProduct $product=null): array
    {
        $businessId=Business::firstOrFail()->id;
        if (! $request->filled('processing_fee_payment_mode')) $request->merge(['processing_fee_payment_mode'=>'financed']);

        $data=$request->validate([
            'name'=>['required','string','max:255',Rule::unique('loan_products')->where(fn($q)=>$q->where('business_id',$businessId))->ignore($product?->id)],
            'minimum_amount'=>['required','decimal:0,2','min:0.01'], 'maximum_amount'=>['required','decimal:0,2','gt:minimum_amount'],
            'duration'=>['required','integer','between:1,5000'], 'duration_unit'=>['required',Rule::in(['days','weeks','months'])],
            'interest_rate'=>['required','decimal:0,6','between:0,1000'], 'interest_calculation_method'=>['required',Rule::in(['flat','reducing_balance','fixed_amount'])],
            'fixed_interest_amount'=>[Rule::requiredIf($request->input('interest_calculation_method')==='fixed_amount'),'nullable','decimal:0,2','min:0'],
            'repayment_frequency'=>['required',Rule::in(['daily','weekly','biweekly','monthly','lump_sum','custom'])], 'grace_period_days'=>['required','integer','between:0,3650'],
            'late_payment_penalty_type'=>['required',Rule::in(['none','fixed','percentage','daily_percentage','weekly_percentage'])], 'late_payment_penalty_value'=>['required','decimal:0,6','between:0,1000000000000000'],
            'processing_fee_type'=>['required',Rule::in(['fixed','percentage'])], 'processing_fee_value'=>['required','decimal:0,6','between:0,1000000000000000'],
            'processing_fee_payment_mode'=>['required',Rule::in(array_keys(LoanProduct::PROCESSING_FEE_PAYMENT_MODES))],
            'insurance_fee_type'=>['required',Rule::in(['fixed','percentage'])], 'insurance_fee_value'=>['required','decimal:0,6','between:0,1000000000000000'],
            'other_charges'=>['required','decimal:0,2','between:0,9999999999999999.99'],
            'payment_deadline_enabled'=>['nullable','boolean'],
            'payment_deadline_time'=>[Rule::requiredIf($request->boolean('payment_deadline_enabled')),'nullable','date_format:H:i'],
            'collateral_required'=>['nullable','boolean'], 'guarantor_required'=>['nullable','boolean'], 'status'=>['required',Rule::in(['active','inactive'])],
        ]);

        foreach ([['processing_fee_type','processing_fee_value'],['insurance_fee_type','insurance_fee_value']] as [$type,$value]) {
            if (($data[$type]??null)==='percentage' && (float)$data[$value] > 1000) throw ValidationException::withMessages([$value=>'Percentage values cannot exceed 1000%.']);
        }
        if (($data['processing_fee_payment_mode'] ?? null)==='deducted') {
            if (($data['processing_fee_type'] ?? null)==='percentage' && (float)$data['processing_fee_value'] >= 100) throw ValidationException::withMessages(['processing_fee_value'=>'A fee deducted from disbursement must be below 100% so the customer receives a positive amount.']);
            if (($data['processing_fee_type'] ?? null)==='fixed' && (float)$data['processing_fee_value'] >= (float)$data['minimum_amount']) throw ValidationException::withMessages(['processing_fee_value'=>'A fixed fee deducted from disbursement must be less than the product minimum loan amount.']);
        }
        if (in_array($data['late_payment_penalty_type'],['percentage','daily_percentage','weekly_percentage'],true) && (float)$data['late_payment_penalty_value'] > 1000) {
            throw ValidationException::withMessages(['late_payment_penalty_value'=>'Percentage penalty values cannot exceed 1000%.']);
        }

        $data['payment_deadline_enabled']=$request->boolean('payment_deadline_enabled');
        $data['payment_deadline_time']=$data['payment_deadline_enabled'] ? $data['payment_deadline_time'] : null;
        $data['collateral_required']=$request->boolean('collateral_required');
        $data['guarantor_required']=$request->boolean('guarantor_required');
        return $data;
    }
}
