<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use App\Models\AccountLedger;
use App\Models\Loan;
use App\Models\LoanPayment;
use App\Models\UserAccount;
use App\Services\AccountService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class LoanController extends Controller
{
    private function getLoan($loan)
    {
        return Loan::where('user_id', auth()->id())
            ->findOrFail($loan->id ?? $loan);
    }

    public function index(Request $request)
    {
        $userId = auth()->id();

        $q = Loan::query()
            ->where('user_id', $userId)
            ->with('account:id,name,type');

        if ($request->filled('status')) {
            $q->where('status', $request->status);
        }

        if ($request->filled('search')) {
            $s = trim($request->search);
            $q->where(function ($qq) use ($s) {
                $qq->where('lender_name', 'like', "%{$s}%")
                    ->orWhere('loan_no', 'like', "%{$s}%");
            });
        }

        $loans = $q->latest('id')->paginate(20)->withQueryString();

        $activeCount = Loan::where('user_id', $userId)
            ->where('status', 'active')
            ->count();

        $activeOutstanding = (float) Loan::where('user_id', $userId)
            ->where('status', 'active')
            ->sum('remaining_amount');

        $totalPrincipal = (float) Loan::where('user_id', $userId)->sum('principal_amount');
        $totalPaidPrincipal = (float) LoanPayment::where('user_id', $userId)->sum('principal_paid');
        $totalPaidInterest = (float) LoanPayment::where('user_id', $userId)->sum('interest_paid');
        $totalPaidFee = (float) LoanPayment::where('user_id', $userId)->sum('fee_paid');

        return view('user.loans.index', compact(
            'loans',
            'activeCount',
            'activeOutstanding',
            'totalPrincipal',
            'totalPaidPrincipal',
            'totalPaidInterest',
            'totalPaidFee'
        ));
    }

    public function create()
    {
        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get(['id', 'name', 'type']);

        return view('user.loans.create', compact('accounts'));
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'user_account_id'  => ['required', 'integer'],
            'principal_amount' => ['required', 'numeric', 'min:0.01'],
            'lender_name'      => ['nullable', 'string', 'max:200'],
            'loan_no'          => ['nullable', 'string', 'max:100'],
            'interest_rate'    => ['nullable', 'numeric', 'min:0'],
            'start_date'       => ['nullable', 'date', 'before_or_equal:today'],
            'note'             => ['nullable', 'string', 'max:4000'],
            'posted_at'        => ['nullable', 'date', 'before_or_equal:now'],
        ]);

        $account = UserAccount::where('id', (int) $data['user_account_id'])
            ->where('user_id', auth()->id())
            ->where('status', 'active')
            ->firstOrFail();

        $postedAt = !empty($data['posted_at'])
            ? Carbon::parse($data['posted_at'])
            : now();

        $startDate = !empty($data['start_date'])
            ? Carbon::parse($data['start_date'])->toDateString()
            : $postedAt->toDateString();

        $trxNo = 'LOAN-' . now()->format('ymdHis') . '-' . random_int(100, 999);

        DB::transaction(function () use ($data, $account, $postedAt, $startDate, $trxNo) {

            $principal = round((float) $data['principal_amount'], 2);

            $loan = Loan::create([
                'user_id'          => auth()->id(),
                'user_account_id'  => $account->id,
                'lender_name'      => $data['lender_name'] ?? null,
                'loan_no'          => $data['loan_no'] ?? null,
                'principal_amount' => $principal,
                'remaining_amount' => $principal,
                'interest_rate'    => round((float) ($data['interest_rate'] ?? 0), 4),
                'start_date'       => $startDate,
                'status'           => 'active',
                'note'             => $data['note'] ?? null,
            ]);

            AccountService::postLedger(
                auth()->id(),
                $account->id,
                'loan_in',
                $trxNo,
                $principal,
                0,
                __('Loan_Controller.messages.loan_received') . (!empty($data['lender_name']) ? ': ' . $data['lender_name'] : ''),
                $postedAt,
                Loan::class,
                $loan->id
            );
        });

        return redirect()
            ->route('user.loans.index')
            ->with('ok', __('Loan_Controller.messages.loan_created_successfully'));
    }

    public function show($loanId)
    {
        $loan = $this->getLoan($loanId);

        return redirect()->route('user.loans.statement', $loan);
    }

    public function edit($loanId)
    {
        $loan = $this->getLoan($loanId);

        if ($loan->status === 'closed') {
            return redirect()
                ->route('user.loans.index')
                ->with('err', __('Loan_Controller.messages.closed_loan_cannot_be_edited'));
        }

        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get(['id', 'name', 'type']);

        return view('user.loans.edit', compact('loan', 'accounts'));
    }

    public function update(Request $request, $loanId)
    {
        $loan = $this->getLoan($loanId);

        $data = $request->validate([
            'user_account_id'  => ['required', 'integer'],
            'principal_amount' => ['required', 'numeric', 'min:0.01'],
            'lender_name'      => ['nullable', 'string', 'max:200'],
            'loan_no'          => ['nullable', 'string', 'max:100'],
            'interest_rate'    => ['nullable', 'numeric', 'min:0'],
            'start_date'       => ['nullable', 'date', 'before_or_equal:today'],
            'note'             => ['nullable', 'string', 'max:4000'],
        ]);

        if ($loan->status === 'closed') {
            return back()
                ->with('err', __('Loan_Controller.messages.closed_loan_cannot_be_edited'))
                ->withInput();
        }

        $account = UserAccount::where('id', (int) $data['user_account_id'])
            ->where('user_id', auth()->id())
            ->where('status', 'active')
            ->firstOrFail();

        $paidPrincipal = (float) LoanPayment::where('user_id', auth()->id())
            ->where('loan_id', $loan->id)
            ->sum('principal_paid');

        $newPrincipal = round((float) $data['principal_amount'], 2);

        if ($newPrincipal + 0.0001 < $paidPrincipal) {
            return back()
                ->with('err', __('Loan_Controller.messages.principal_cannot_be_less_than_paid_amount'))
                ->withInput();
        }

        DB::transaction(function () use ($loan, $data, $account, $newPrincipal, $paidPrincipal) {

            $loan->update([
                'user_account_id'  => $account->id,
                'lender_name'      => $data['lender_name'] ?? null,
                'loan_no'          => $data['loan_no'] ?? null,
                'principal_amount' => $newPrincipal,
                'remaining_amount' => round(max(0, $newPrincipal - $paidPrincipal), 2),
                'interest_rate'    => round((float) ($data['interest_rate'] ?? 0), 4),
                'start_date'       => !empty($data['start_date']) ? $data['start_date'] : $loan->start_date,
                'note'             => $data['note'] ?? null,
            ]);

            $loanLedger = AccountLedger::query()
                ->where('user_id', auth()->id())
                ->where('reference_type', Loan::class)
                ->where('reference_id', $loan->id)
                ->where('type', 'loan_in')
                ->orderBy('id')
                ->first();

            if ($loanLedger) {
                $loanLedger->update([
                    'user_account_id' => $account->id,
                    'debit'           => $newPrincipal,
                    'credit'          => 0,
                    'description'     => __('Loan_Controller.messages.loan_received') . (!empty($data['lender_name']) ? ': ' . $data['lender_name'] : ''),
                ]);
            }
        });

        return redirect()
            ->route('user.loans.index')
            ->with('ok', __('Loan_Controller.messages.loan_updated_successfully'));
    }

    public function statement(Request $request, $loanId)
    {
        $loan = $this->getLoan($loanId);

        $paymentsQuery = LoanPayment::query()
            ->where('user_id', auth()->id())
            ->where('loan_id', $loan->id)
            ->with('account:id,name,type');

        if ($request->filled('from')) {
            $paymentsQuery->whereDate('paid_at', '>=', $request->from);
        }

        if ($request->filled('to')) {
            $paymentsQuery->whereDate('paid_at', '<=', $request->to);
        }

        $payments = (clone $paymentsQuery)
            ->latest('paid_at')
            ->latest('id')
            ->paginate(20)
            ->withQueryString();

        $totalPrincipalPaid = round((float) (clone $paymentsQuery)->sum('principal_paid'), 2);
        $totalInterestPaid  = round((float) (clone $paymentsQuery)->sum('interest_paid'), 2);
        $totalFeePaid       = round((float) (clone $paymentsQuery)->sum('fee_paid'), 2);
        $totalPaid          = round((float) (clone $paymentsQuery)->sum('total_paid'), 2);

        $loanPaymentIds = LoanPayment::query()
            ->where('user_id', auth()->id())
            ->where('loan_id', $loan->id)
            ->pluck('id');

        $ledgerEntries = AccountLedger::query()
            ->where('user_id', auth()->id())
            ->where(function ($q) use ($loan, $loanPaymentIds) {
                $q->where(function ($qq) use ($loan) {
                    $qq->where('reference_type', Loan::class)
                        ->where('reference_id', $loan->id);
                });

                if ($loanPaymentIds->isNotEmpty()) {
                    $q->orWhere(function ($qq) use ($loanPaymentIds) {
                        $qq->where('reference_type', LoanPayment::class)
                            ->whereIn('reference_id', $loanPaymentIds);
                    });
                }
            })
            ->with('account:id,name,type')
            ->orderBy('posted_at')
            ->orderBy('id')
            ->get();

        return view('user.loans.statement', compact(
            'loan',
            'payments',
            'ledgerEntries',
            'totalPrincipalPaid',
            'totalInterestPaid',
            'totalFeePaid',
            'totalPaid'
        ));
    }

    public function close($loanId)
    {
        $loan = $this->getLoan($loanId);

        if ($loan->status === 'closed') {
            return back()->with('err', __('Loan_Controller.messages.loan_already_closed'));
        }

        if ((float) $loan->remaining_amount > 0.01) {
            return back()->with('err', __('Loan_Controller.messages.loan_cannot_be_closed_until_remaining_principal_zero'));
        }

        $loan->update([
            'status' => 'closed',
            'closed_at' => now(),
            'remaining_amount' => 0,
        ]);

        return back()->with('ok', __('Loan_Controller.messages.loan_closed_successfully'));
    }

    public function destroy($loanId)
    {
        $loan = $this->getLoan($loanId);

        $hasPayments = LoanPayment::where('user_id', auth()->id())
            ->where('loan_id', $loan->id)
            ->exists();

        if ($hasPayments) {
            return back()->with('err', __('Loan_Controller.messages.loan_cannot_be_deleted_because_payments_exist'));
        }

        DB::transaction(function () use ($loan) {
            AccountService::deleteLedgerByReference(auth()->id(), Loan::class, $loan->id);
            $loan->delete();
        });

        return redirect()
            ->route('user.loans.index')
            ->with('ok', __('Loan_Controller.messages.loan_deleted_successfully'));
    }
}