<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
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;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

class LoanPaymentController extends Controller
{
    public function create($loanId)
    {
        $loan = Loan::where('user_id', auth()->id())->findOrFail($loanId);

        if ($loan->status === 'closed') {
            return back()->with('err', 'Closed loan এ payment দেওয়া যাবে না।');
        }

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

        [$baseDate, $days, $interestDue] = $this->loanInterestPreview($loan, now());

        $remaining = round((float) $loan->remaining_amount, 2);

        return view('user.loans.payments.create', compact(
            'loan',
            'accounts',
            'baseDate',
            'days',
            'interestDue',
            'remaining'
        ));
    }

    public function store(Request $request, $loanId)
    {
        $loan = Loan::where('user_id', auth()->id())->findOrFail($loanId);

        if ($loan->status === 'closed') {
            return back()->with('err', 'Closed loan এ payment দেওয়া যাবে না।');
        }

        $data = $request->validate([
            'user_account_id' => ['required', 'integer'],
            'paid_at'         => ['nullable', 'date', 'before_or_equal:now'],
            'note'            => ['nullable', 'string', 'max:2000'],
            'total_paid'      => ['required', 'numeric', 'min:0.01'],
            'interest_paid'   => ['nullable', 'numeric', 'min:0'],
            'principal_paid'  => ['nullable', 'numeric', 'min:0'],
            'fee_paid'        => ['nullable', 'numeric', 'min:0'],
            'proof_photo'     => ['nullable', 'file', 'mimes:jpg,jpeg,png,webp,pdf', 'max:4096'],
        ]);

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

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

        $lastPaidAt = LoanPayment::where('user_id', auth()->id())
            ->where('loan_id', $loan->id)
            ->max('paid_at');

        $minDate = $lastPaidAt
            ? Carbon::parse($lastPaidAt)
            : ($loan->start_date ? Carbon::parse($loan->start_date) : Carbon::parse($loan->created_at));

        if ($paidAt->lt($minDate)) {
            return back()
                ->with('err', 'Payment date last payment/start date-এর আগে হতে পারবে না।')
                ->withInput();
        }

        try {
            DB::transaction(function () use ($request, $loan, $payAccount, $paidAt, $data) {

                $remainingBefore = round((float) $loan->remaining_amount, 2);
                $totalPaidInput  = round((float) $data['total_paid'], 2);
                $feePaid         = round((float) ($data['fee_paid'] ?? 0), 2);

                [$baseDate, $days, $autoInterest] = $this->loanInterestPreview($loan, $paidAt);

                $interestPaid = isset($data['interest_paid']) && $data['interest_paid'] !== null
                    ? round((float) $data['interest_paid'], 2)
                    : round(min($totalPaidInput, $autoInterest), 2);

                $principalPaid = isset($data['principal_paid']) && $data['principal_paid'] !== null
                    ? round((float) $data['principal_paid'], 2)
                    : round(max(0, $totalPaidInput - $interestPaid - $feePaid), 2);

                $calculatedTotal = round($principalPaid + $interestPaid + $feePaid, 2);

                if ($principalPaid <= 0 && $interestPaid <= 0 && $feePaid <= 0) {
                    throw new \RuntimeException('Payment breakdown invalid.');
                }

                if ($interestPaid < 0 || $principalPaid < 0 || $feePaid < 0) {
                    throw new \RuntimeException('Negative amount allowed না।');
                }

                if ($principalPaid > $remainingBefore + 0.0001) {
                    throw new \RuntimeException('Principal paid remaining amount-এর বেশি হতে পারবে না।');
                }

                if (abs($calculatedTotal - $totalPaidInput) > 0.01) {
                    throw new \RuntimeException('Total paid এবং breakdown এক না।');
                }

                $proof = null;
                if ($request->hasFile('proof_photo')) {
                    $proof = $this->uploadPublicFile($request->file('proof_photo'), 'uploads/loan_payments');
                }

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

                $payment = LoanPayment::create([
                    'user_id'         => auth()->id(),
                    'loan_id'         => $loan->id,
                    'user_account_id' => $payAccount->id,
                    'principal_paid'  => $principalPaid,
                    'interest_paid'   => $interestPaid,
                    'fee_paid'        => $feePaid,
                    'total_paid'      => $calculatedTotal,
                    'trx_no'          => $trxNo,
                    'paid_at'         => $paidAt,
                    'note'            => $data['note'] ?? null,
                    'proof_photo'     => $proof,
                ]);

                $loan->update([
                    'remaining_amount' => round(max(0, $remainingBefore - $principalPaid), 2),
                ]);

                AccountService::postLedger(
                    auth()->id(),
                    $payAccount->id,
                    'loan_payment_out',
                    $trxNo,
                    0,
                    $calculatedTotal,
                    "Loan payment (P: {$principalPaid}, I: {$interestPaid}, F: {$feePaid})",
                    $paidAt,
                    LoanPayment::class,
                    $payment->id
                );

                if ($loan->fresh()->remaining_amount <= 0.01) {
                    $loan->update([
                        'remaining_amount' => 0,
                        'status' => 'closed',
                        'closed_at' => now(),
                    ]);
                }
            });
        } catch (\Throwable $e) {
            return back()->with('err', $e->getMessage())->withInput();
        }

        return redirect()
            ->route('user.loans.statement', $loan->id)
            ->with('ok', 'Payment saved successfully.');
    }

    public function destroy($loanId, $paymentId)
    {
        $loan = Loan::where('user_id', auth()->id())->findOrFail($loanId);

        $payment = LoanPayment::where('user_id', auth()->id())
            ->where('loan_id', $loan->id)
            ->findOrFail($paymentId);

        DB::transaction(function () use ($loan, $payment) {

            $loan->update([
                'remaining_amount' => round($loan->remaining_amount + $payment->principal_paid, 2),
                'status' => 'active',
                'closed_at' => null,
            ]);

            AccountService::deleteLedgerByReference(auth()->id(), LoanPayment::class, $payment->id);

            if ($payment->proof_photo) {
                $this->deletePublicFile($payment->proof_photo);
            }

            $payment->delete();
        });

        return back()->with('ok', 'Payment deleted successfully.');
    }
    
    private function loanInterestPreview($loan, $asOfDate): array
    {
        $asOfDate = \Carbon\Carbon::parse($asOfDate);

        $lastPaidAt = \App\Models\LoanPayment::where('user_id', auth()->id())
           ->where('loan_id', $loan->id)
           ->max('paid_at');

        $baseDate = $lastPaidAt
            ? \Carbon\Carbon::parse($lastPaidAt)
            : ($loan->start_date
                ? \Carbon\Carbon::parse($loan->start_date)
                : \Carbon\Carbon::parse($loan->created_at));

        $baseDate = $baseDate->startOfDay();
        $asOfDay  = $asOfDate->startOfDay();

        if ($asOfDay->lt($baseDate)) {
            return [$baseDate, 0, 0];
        }

        $days = $baseDate->diffInDays($asOfDay);
        $rate = (float) $loan->interest_rate;
        $remaining = (float) $loan->remaining_amount;

        $interestDue = 0;

        if ($rate > 0 && $remaining > 0 && $days > 0) {
            $interestDue = round($remaining * ($rate / 100) * ($days / 365), 2);
        }

        return [$baseDate, $days, $interestDue];
    }
}