<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\Customer;
use App\Models\Loan;
use App\Models\LoanProduct;
use App\Services\AuditService;
use App\Services\LoanService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;

class LoanController extends Controller
{
    public function __construct(
        protected AuditService $auditService,
        protected LoanService $loanService,
    ) {}

    public function index(Request $request): View
    {
        $query = Loan::query()->with(['customer', 'loanProduct', 'schedules']);

        if ($status = $request->input('status')) {
            $query->where('status', $status);
        }

        if ($productId = $request->input('product_id')) {
            $query->where('product_id', $productId);
        }

        $loans = $query->latest()->paginate(20)->withQueryString();
        $products = LoanProduct::query()->where('status', 'active')->orderBy('name')->get();

        return view('admin.loans.index', compact('loans', 'products'));
    }

    public function create(): View
    {
        $customers = Customer::query()->where('status', 'active')->orderBy('customer_no')->get();
        $products = LoanProduct::query()->where('status', 'active')->orderBy('name')->get();
        $branches = Branch::query()->where('status', 'active')->orderBy('name')->get();

        return view('admin.loans.create', compact('customers', 'products', 'branches'));
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'customer_id' => ['required', 'exists:customers,id'],
            'product_id' => ['required', 'exists:loan_products,id'],
            'branch_id' => ['nullable', 'exists:branches,id'],
            'account_id' => ['nullable', 'exists:accounts,id'],
            'principal' => ['required', 'numeric', 'min:1'],
            'interest_rate' => ['nullable', 'numeric', 'min:0'],
            'interest_method' => ['nullable', 'in:reducing,flat,discount'],
            'tenor_months' => ['required', 'integer', 'min:1'],
            'processing_fee' => ['nullable', 'numeric', 'min:0'],
            'purpose' => ['nullable', 'string', 'max:255'],
            'credit_score' => ['nullable', 'integer', 'min:0', 'max:900'],
            'officer_id' => ['nullable', 'exists:users,id'],
        ]);

        $product = LoanProduct::query()->findOrFail($validated['product_id']);

        $loan = Loan::query()->create([
            'uuid' => (string) Str::uuid(),
            'loan_no' => $this->generateLoanNo(),
            'customer_id' => $validated['customer_id'],
            'product_id' => $validated['product_id'],
            'branch_id' => $validated['branch_id'] ?? null,
            'account_id' => $validated['account_id'] ?? null,
            'principal' => $validated['principal'],
            'interest_rate' => $validated['interest_rate'] ?? $product->interest_rate,
            'interest_method' => $validated['interest_method'] ?? $product->interest_method,
            'tenor_months' => $validated['tenor_months'],
            'processing_fee' => $validated['processing_fee']
                ?? round($validated['principal'] * ((float) $product->processing_fee_pct / 100), 2),
            'outstanding_principal' => $validated['principal'],
            'purpose' => $validated['purpose'] ?? null,
            'application_date' => now()->toDateString(),
            'status' => 'submitted',
            'credit_score' => $validated['credit_score'] ?? null,
            'officer_id' => $validated['officer_id'] ?? auth()->id(),
            'created_by' => auth()->id(),
        ]);

        $this->auditService->log('create', 'loans', 'loan', $loan->id, null, $loan->toArray());

        return redirect()
            ->route('admin.loans.show', $loan)
            ->with('success', 'Loan application submitted successfully.');
    }

    public function show(Loan $loan): View
    {
        $loan->load([
            'customer',
            'loanProduct',
            'schedules',
            'guarantors',
            'collaterals',
            'repayments',
        ]);

        return view('admin.loans.show', compact('loan'));
    }

    public function approve(Loan $loan): RedirectResponse
    {
        if (! in_array($loan->status, ['underwriting', 'submitted'], true)) {
            return back()->withErrors(['loan' => 'Only submitted or underwriting loans can be approved.']);
        }

        $old = $loan->toArray();

        $loan->update([
            'status' => 'approved',
            'approved_by' => auth()->id(),
            'approval_date' => now()->toDateString(),
        ]);

        $this->loanService->generateAmortizationSchedule($loan->fresh());

        $this->auditService->log('approve', 'loans', 'loan', $loan->id, $old, $loan->fresh()->toArray());

        return back()->with('success', 'Loan approved successfully.');
    }

    public function reject(Request $request, Loan $loan): RedirectResponse
    {
        if (! in_array($loan->status, ['underwriting', 'submitted'], true)) {
            return back()->withErrors(['loan' => 'Only submitted or underwriting loans can be rejected.']);
        }

        $validated = $request->validate([
            'rejection_reason' => ['required', 'string', 'max:1000'],
        ]);

        $old = $loan->toArray();

        $loan->update([
            'status' => 'rejected',
            'rejection_reason' => $validated['rejection_reason'],
        ]);

        $this->auditService->log('reject', 'loans', 'loan', $loan->id, $old, $loan->fresh()->toArray());

        return back()->with('success', 'Loan rejected.');
    }

    protected function generateLoanNo(): string
    {
        $year = date('Y');

        $last = Loan::query()
            ->where('loan_no', 'like', "LN-{$year}-%")
            ->orderByDesc('id')
            ->value('loan_no');

        $next = 1;

        if ($last && preg_match('/LN-\d{4}-(\d+)$/', $last, $matches)) {
            $next = (int) $matches[1] + 1;
        }

        return 'LN-'.$year.'-'.str_pad((string) $next, 4, '0', STR_PAD_LEFT);
    }
}
