<?php

namespace App\Http\Controllers;
use App\Loan;
use App\Customer;
use App\Installment;
use App\LoanPayment;
use App\BankAccount;
use App\LoanSaving;
use App\LoanSavingsWithdrawal;
use App\Services\LoanService;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;

class LoanController extends Controller
{
    public function index(Request $request)
    {
        $query = Loan::with('customer');

        if ($request->filled('customer_id')) {
            $query->where('customer_id', $request->customer_id);
        }

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

        if ($request->filled('start_date') && $request->filled('end_date')) {
            $query->whereBetween('issue_date', [$request->start_date, $request->end_date]);
        }

        $loans = $query->latest()->paginate(15)->withQueryString(); 
        $customers = \App\Customer::all(); 

        return view('pages.loans.index', compact('loans', 'customers'));
    }


    public function create()
    {
        $customers = Customer::all(); 
        $bank = BankAccount::all(); 
        return view('pages.loans.create', compact('customers','bank'));
    }


    /**
     * Issue New Loan
     */
    public function store(Request $request, LoanService $loanService)
    {
        $request->validate([
            'customer_id'        => 'required|exists:customers,id',
            'loan_amount'        => 'required|numeric|min:1',
            'interest_rate'      => 'required|numeric|min:0',
            'installment_type'   => 'required|in:daily,weekly,monthly',
            'installment_count'  => 'required|integer|min:1',
            'issue_date'         => 'required|date',
            'first_installment_date' => 'nullable|date',
            'savings_amount'     => 'nullable|numeric|min:0',
            'bank_account_id'    => 'required|exists:bank_accounts,id',
        ]);

        DB::transaction(function () use ($request, $loanService) {

            // 1️⃣ Create Loan
            $loan = $loanService->issueLoan($request->all());

            $savingsAmount = $request->savings_amount ?? 0;

            if ($savingsAmount > 0) {

                $bankAccount = BankAccount::findOrFail($request->bank_account_id);

                // 2️⃣ Store Loan Saving
                LoanSaving::create([
                    'loan_id'        => $loan->id,
                    'amount'         => $savingsAmount,
                    'bank_account_id'=> $bankAccount->id,
                    'user_id'        => auth()->id(),
                    'created_at'     => $request->issue_date,
                ]);

                // 3️⃣ Update bank balance
                $bankAccount->current_balance += $savingsAmount;
                $bankAccount->save();
            }
        });

        return redirect()
            ->route('loans.index')
            ->with('success', 'Loan issued successfully with savings');
    }

    public function edit($id)
    {
        $loan = Loan::findOrFail($id);
        return view('pages.loans.edit', compact('loan'));
    }


   public function update(Request $request, Loan $loan, LoanService $loanService)
    {
        $request->validate([
            'loan_amount'        => 'required|numeric|min:1',
            'interest_rate'      => 'required|numeric|min:0',
            'installment_type'   => 'required|in:daily,weekly,monthly',
            'installment_count'  => 'required|integer|min:1',
            'issue_date'         => 'required|date',
        ]);

        $loanService->updateLoan($loan, $request->only([
            'loan_amount',
            'interest_rate',
            'installment_type',
            'installment_count',
            'issue_date'
        ]));

        return redirect()->route('loans.index')->with('success', 'Loan updated successfully.');
    }

    /**
     * Loan Details
     */
    public function show($id)
    {
       $loan = Loan::with('customer', 'installments', 'savings')->findOrFail($id);
       $totalSavings = $loan->totalSavings();
       $loans = Loan::where('customer_id', $loan->customer_id)->get();
        return view('pages.loans.show', compact('loan','totalSavings', 'loans'));
    }

    public function installments($id)
    {
        $loan = Loan::with(['customer', 'installments.payments', 'savings'])->findOrFail($id);
         $totalSavings = $loan->totalSavings();
        return view('pages.loans.installments', compact('loan','totalSavings'));
    }
    public function paymentReport($id)
    {
        $loan = Loan::with(['customer', 'installments.payments.bankAccount', 'savings', 'savingsWithdrawals'])
                    ->findOrFail($id);

        $totalSavings = $loan->totalSavings();

        // Flatten all payments and group by created_at
        $payments = $loan->installments->flatMap(function($installment) {
            return $installment->payments->map(function($payment) use ($installment) {
                return [
                    'installment_no' => $installment->installment_no,
                    'amount' => $payment->amount,
                    'created_at' => $payment->created_at,
                    'payment_date' => $payment->payment_date,
                    'bank_account_name' => $payment->bankAccount->name,
                ];
            });
        });

        // Group by created_at and sum amounts
        $groupedPayments = $payments->groupBy(function($item) {
            return $item['created_at']->format('Y-m-d H:i:s'); 
        })->map(function($items) {
            return [
                'installment_no' => $items->first()['installment_no'],
                'total_amount' => $items->sum('amount'),
                'payment_date' => $items->first()['payment_date'],
                'bank_account_name' => $items->first()['bank_account_name'],
            ];
        });

        return view('pages.loans.payments', compact('loan', 'totalSavings', 'groupedPayments'));
    }



    /**
     * Collect Installment Payment (supports partial)
     */
   public function collectPayment(Request $request, $installmentId)
    {
        $request->validate([
            'amount' => 'nullable|numeric|min:1',
            'savings_amount' => 'nullable|numeric|min:0',
            'paid_date' => 'required|date',
            'bank_account_id' => 'required|exists:bank_accounts,id',
        ]);

        DB::transaction(function () use ($request, $installmentId) {
            $loanPayAmount = $request->amount;
            $savingsAmount = $request->savings_amount ?? 0;
            $bankAccount = BankAccount::findOrFail($request->bank_account_id);
            $installment = Installment::with('loan')->findOrFail($installmentId);

            // Keep track of first payment to link savings
            $firstPayment = null;

            // 1️⃣ Pay loan installments
            $installments = Installment::where('loan_id', $installment->loan_id)
                ->where('status', '!=', 'paid')
                ->orderBy('installment_no')
                ->get();

            foreach ($installments as $inst) {
                if ($loanPayAmount <= 0) break;

                $remaining = $inst->total_amount - $inst->paid_amount;

                if ($loanPayAmount >= $remaining) {
                    $inst->update([
                        'paid_amount' => $inst->total_amount,
                        'status' => 'paid',
                        'paid_date' => $request->paid_date,
                    ]);

                    $payment = LoanPayment::create([
                        'loan_id' => $inst->loan_id,
                        'installment_id' => $inst->id,
                        'amount' => $remaining,
                        'payment_date' => $request->paid_date,
                        'bank_account_id' => $bankAccount->id,
                        'user_id' => auth()->id(),
                    ]);

                    if (!$firstPayment) {
                        $firstPayment = $payment;
                    }

                    $bankAccount->current_balance += $remaining;
                    $loanPayAmount -= $remaining;

                } else {
                    $inst->update([
                        'paid_amount' => $inst->paid_amount + $loanPayAmount,
                        'status' => 'pending',
                        'paid_date' => $request->paid_date,
                    ]);

                    $payment = LoanPayment::create([
                        'loan_id' => $inst->loan_id,
                        'installment_id' => $inst->id,
                        'amount' => $loanPayAmount,
                        'payment_date' => $request->paid_date,
                        'bank_account_id' => $bankAccount->id,
                        'user_id' => auth()->id(),
                    ]);

                    if (!$firstPayment) {
                        $firstPayment = $payment;
                    }

                    $bankAccount->current_balance += $loanPayAmount;
                    $loanPayAmount = 0;
                }
            }

            // 2️⃣ Save savings linked to the first payment
            if ($savingsAmount > 0 && $firstPayment) {
                LoanSaving::create([
                    'loan_id' => $installment->loan_id,
                    'loan_payment_id' => $firstPayment->id,
                    'amount' => $savingsAmount,
                    'created_at' => $request->paid_date,
                    'user_id' => auth()->id(),
                ]);

                $bankAccount->current_balance += $savingsAmount;
            }

            $bankAccount->save();
        });

        return redirect()->back()->with('success', 'Payment collected and savings added successfully.');
    }

    public function collectSavings(Request $request, $loanId)
    {
        $request->validate([
            'savings_amount' => 'required|numeric|min:1',
            'paid_date' => 'required|date',
            'bank_account_id' => 'required|exists:bank_accounts,id',
        ]);

        DB::transaction(function () use ($request, $loanId) {
            $loan = Loan::findOrFail($loanId);
            $bankAccount = BankAccount::findOrFail($request->bank_account_id);

            // Save savings directly
            LoanSaving::create([
                'loan_id' => $loan->id,
                'amount' => $request->savings_amount,
                'created_at' => $request->paid_date,
                'user_id' => auth()->id(),
                'bank_account_id' => $bankAccount->id,
            ]);

            // Update bank account balance
            $bankAccount->current_balance += $request->savings_amount;
            $bankAccount->save();
        });

        return redirect()->back()->with('success', 'Savings added successfully.');
    }

    public function deleteSavings($id)
    {
        DB::transaction(function () use ($id) {
            $saving = LoanSaving::findOrFail($id);

            // Adjust bank balance
            if ($saving->bank_account_id) {
                $bank = BankAccount::findOrFail($saving->bank_account_id);
                $bank->current_balance -= $saving->amount;
                $bank->save();
            }

            $saving->delete();
        });

        return back()->with('success', 'Savings deleted successfully.');
    }

    public function deleteSavingsWithdrawal($id)
    {
        DB::transaction(function () use ($id) {
            $withdrawal = LoanSavingsWithdrawal::findOrFail($id); //

            // Adjust bank balance if needed
            if ($withdrawal->bank_account_id) {
                $bank = BankAccount::findOrFail($withdrawal->bank_account_id);
                $bank->current_balance += $withdrawal->amount; 
                $bank->save();
            }

            $withdrawal->delete();
        });

        return back()->with('success', 'Withdrawal deleted successfully.');
    }




    public function showWithdrawForm(Request $request)
    {
        $loanId = $request->query('loan_id');
        $loans = \App\Loan::with('savings')->get(); // all loans
        $selectedLoan = $loanId ? \App\Loan::with('savings')->find($loanId) : null;

        $bankAccounts = \App\BankAccount::all();
        
        // Calculate total savings if a loan is selected
        $totalSavings = $selectedLoan ? $selectedLoan->totalSavings() : 0;

        return view('pages.loans.withdraw', compact('loans', 'selectedLoan', 'bankAccounts', 'totalSavings'));
    }

    public function withdrawLoanSavings(Request $request)
    {
        $request->validate([
            'loan_id' => 'required|exists:loans,id',
            'amount' => 'required|numeric|min:1',
            'bank_account_id' => 'required|exists:bank_accounts,id',
            'withdraw_date' => 'required|date',
        ]);

        DB::transaction(function () use ($request) {
            $loan = Loan::with('savings')->findOrFail($request->loan_id);
            $available = $loan->totalSavings();

            if ($request->amount > $available) {
                throw new \Exception("Withdraw amount exceeds available savings.");
            }

            // Create withdrawal record
            LoanSavingsWithdrawal::create([
                'loan_id' => $loan->id,
                'amount' => $request->amount,
                'withdraw_date' => $request->withdraw_date,
                'bank_account_id' => $request->bank_account_id,
            ]);

            // Deduct from bank account
            $bank = BankAccount::findOrFail($request->bank_account_id);
            $bank->current_balance -= $request->amount;
            $bank->save();
        });

        return redirect()->back()->with('success', 'Loan savings withdrawn successfully.');
    }
    /**
     * Mark overdue installments (for CRON)
     */
    public function markOverdue()
    {
        $count = Installment::where('status', 'pending')
            ->whereDate('due_date', '<', now())
            ->update(['status' => 'overdue']);

        return response()->json([
            'message' => "$count installments marked overdue"
        ]);
    }


    public function loanReport(Request $request)
    {
        $query = Loan::with(['customer', 'installments']);

        // Filters
        if ($request->filled('customer_id')) {
            $query->where('customer_id', $request->customer_id);
        }

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

        if ($request->filled('start_date') && $request->filled('end_date')) {
            $query->whereBetween('issue_date', [
                $request->start_date,
                $request->end_date
            ]);
        }

        $allLoans = (clone $query)->get();
        $loans = $query->paginate(20)->withQueryString();

        $reportLoans = $loans->getCollection()->map(function ($loan) {
            $totalPayable = $loan->installments->sum('total_amount');
            $totalPaid    = $loan->installments->sum('paid_amount');

            return [
                'loan_id'        => $loan->id,
                'customer'       => $loan->customer->name ?? 'N/A',
                'loan_amount'    => $loan->loan_amount,
                'total_payable'  => $totalPayable,
                'total_paid'     => $totalPaid,
                'total_due'      => $totalPayable - $totalPaid,
                'status'         => $loan->status,
                'issue_date'     => $loan->issue_date,
            ];
        });

        $totalLoanAmount = $allLoans->sum('loan_amount');
        $totalPayable    = $allLoans->sum(fn($loan) => $loan->installments->sum('total_amount'));
        $totalPaid       = $allLoans->sum(fn($loan) => $loan->installments->sum('paid_amount'));
        $totalDue        = $totalPayable - $totalPaid;

        $customers = Customer::all();

        return view('pages.loans.loan_report', compact(
            'loans',
            'reportLoans',
            'customers',
            'totalLoanAmount',
            'totalPayable',
            'totalPaid',
            'totalDue'
        ));
    }


    public function updateStatus(Request $request, Loan $loan)
    {
        $request->validate([
            'status' => 'required|in:pending,accept,completed,overdue',
        ]);

        DB::transaction(function () use ($request, $loan) {
            if ($request->status === 'accept') {
                $bankAccount = BankAccount::where('default', 1)->first();

                if (!$bankAccount) {
                    throw new \Exception('No default bank account set.');
                }
                $bankAccount->current_balance -= $loan->loan_amount;
                $bankAccount->save();
            }

            $loan->status = $request->status;
            $loan->save();
        });

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

    public function installmentsReport(Request $request)
    {
        $installmentType = $request->query('installment_type');
        $customerId = $request->query('customer_id');

        $installments = Installment::with('loan.customer')
            ->where('status', 'pending')
            ->whereHas('loan', function ($query) use ($installmentType, $customerId) {
                $query->where('status', '!=', 'pending');

                if ($installmentType) {
                    $query->where('installment_type', $installmentType);
                }

                if ($customerId) {
                    $query->where('customer_id', $customerId);
                }
            })
            ->orderBy('due_date', 'asc')
            ->get();

        $customers = \App\Customer::orderBy('name')->get();

        return view('pages.loans.installments_report', compact('installments', 'installmentType', 'customers', 'customerId'));
    }

    public function destroy($id)
    {
        $loan = Loan::findOrFail($id);

        DB::transaction(function() use ($loan) {
            $loan->delete();
        });

        return redirect()->route('loans.index')
                         ->with('success', 'Loan and all related data deleted successfully.');
    }
    
    public function transferSavings(Request $request)
    {
        // Validate the incoming request
        $request->validate([
            'source_loan_id' => 'required|exists:loans,id',
            'target_loan_id' => 'required|exists:loans,id|different:source_loan_id',
            'savings_amount' => 'required|numeric|min:1',
        ]);

        DB::transaction(function () use ($request) {
            $sourceLoan = Loan::with('savings')->findOrFail($request->source_loan_id);
            $targetLoan = Loan::findOrFail($request->target_loan_id);

            $availableSavings = $sourceLoan->savings->sum('amount');
            if ($request->savings_amount > $availableSavings) {
                throw new \Exception("The transfer amount exceeds the available savings on the source loan.");
            }
            $remainingSavings = $request->savings_amount;
            foreach ($sourceLoan->savings as $saving) {
                if ($remainingSavings <= 0) {
                    break;
                }

                if ($saving->amount <= $remainingSavings) {
                    LoanSaving::create([
                        'loan_id' => $targetLoan->id,
                        'amount' => $saving->amount,
                        'created_at' => $saving->created_at, 
                        'user_id' => auth()->id(),
                    ]);
                    $remainingSavings -= $saving->amount;
                    $saving->delete();
                } else {
                    
                    LoanSaving::create([
                        'loan_id' => $targetLoan->id,
                        'amount' => $remainingSavings,
                        'created_at' => $saving->created_at, // Use the original `created_at`
                        'user_id' => auth()->id(),
                    ]);

                    // Update the remaining amount in the source savings
                    $saving->amount -= $remainingSavings;
                    $saving->save();

                    // All savings have been transferred
                    $remainingSavings = 0;
                }
            }
        });

        return redirect()->back()->with('success', 'Savings transferred successfully.');
    }


}
