<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\StaffLoan;
use App\Models\StaffLoanDeduction;
use App\Models\Employee;
use App\Models\Company;
use App\Models\MonthlyCompanyFinancial;
use Illuminate\Http\Request;
use Carbon\Carbon;

class StaffLoanController extends Controller
{
    public function index(Request $request)
    {
        $user  = $request->user();
        $query = StaffLoan::with(['employee', 'company', 'approvedBy']);

        if ($user->hasRole('Super Admin')) {
            // see all
        } elseif ($user->company_id) {
            $query->where('company_id', $user->company_id);
        } else {
            return response()->json(['data' => []]);
        }

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

        return response()->json(['data' => $query->latest()->get()]);
    }

    public function store(Request $request)
    {
        $user = $request->user();
        if (!$user->company_id && !$user->hasRole('Super Admin')) {
            return response()->json(['message' => 'Not associated with a company'], 403);
        }

        $validated = $request->validate([
            'employee_id'    => 'required|exists:employees,id',
            'amount'         => 'required|numeric|min:1',
            'interest_rate'  => 'nullable|numeric|min:0|max:100',
            'term_months'    => 'required|integer|min:1|max:120',
            'purpose'        => 'required|string|max:1000',
            'deduction_type' => 'required|in:percentage,fixed',
            'deduction_value'=> 'required|numeric|min:0.01',
            'payment_method' => 'required|in:bank,cash',
            'bank_account_id'=> 'nullable|integer|exists:bank_accounts,id',
            'notes'          => 'nullable|string|max:1000',
        ]);

        // Get company ID from user (or from selected employee for Super Admin)
        $companyId = $user->company_id;
        if ($user->hasRole('Super Admin')) {
            $employee = \App\Models\Employee::find($validated['employee_id']);
            $companyId = $employee?->company_id;
        }

        // Validate bank account belongs to company if payment method is bank
        if ($validated['payment_method'] === 'bank') {
            if (!$validated['bank_account_id']) {
                return response()->json(['message' => 'Bank account is required when payment method is bank'], 422);
            }
            $bankAccount = \App\Models\BankAccount::where('id', $validated['bank_account_id'])
                ->where('company_id', $companyId)
                ->first();
            if (!$bankAccount) {
                return response()->json(['message' => 'Invalid bank account'], 422);
            }
        }

        $employee = Employee::findOrFail($validated['employee_id']);
        $companyId = $user->company_id ?? $employee->company_id;

        // Validate deduction makes sense
        if ($validated['deduction_type'] === 'percentage') {
            if ($validated['deduction_value'] > 50) {
                return response()->json(['message' => 'Deduction percentage cannot exceed 50% of salary'], 422);
            }
            if ($employee->salary) {
                $monthlyDeduction = $employee->salary * ($validated['deduction_value'] / 100);
                if ($monthlyDeduction <= 0) {
                    return response()->json(['message' => 'Calculated monthly deduction is zero. Check employee salary.'], 422);
                }
            }
        }

        $interest     = $validated['interest_rate'] ?? 0;
        $totalInterest = round($validated['amount'] * ($interest / 100 / 12) * $validated['term_months'], 2);
        $totalRepayable = round($validated['amount'] + $totalInterest, 2);

        $loan = StaffLoan::create([
            'loan_number'        => 'SL-' . date('Y') . '-' . strtoupper(substr(uniqid(), -6)),
            'employee_id'        => $validated['employee_id'],
            'company_id'         => $companyId,
            'principal_amount'   => $validated['amount'],
            'outstanding_balance'=> $validated['amount'],
            'interest_rate'      => $interest,
            'term_months'        => $validated['term_months'],
            'purpose'            => $validated['purpose'],
            'deduction_type'     => $validated['deduction_type'],
            'deduction_value'    => $validated['deduction_value'],
            'status'             => 'pending',
            'notes'              => $validated['notes'] ?? null,
            'payment_method'     => $validated['payment_method'],
            'bank_account_id'    => $validated['bank_account_id'] ?? null,
        ]);

        return response()->json([
            'data'    => $loan->load(['employee', 'company']),
            'message' => 'Staff loan request created — pending approval',
        ], 201);
    }

    public function approve(Request $request, $id)
    {
        $user = $request->user();
        if (!$user->hasRole('Super Admin') && !$user->hasRole('Company Admin') && !$user->hasRole('Finance Manager')) {
            return response()->json(['message' => 'Unauthorized'], 403);
        }

        $loan = StaffLoan::findOrFail($id);
        if ($loan->status !== 'pending') {
            return response()->json(['message' => 'Loan is not pending'], 400);
        }

        // Validate approval payment method (cash or bank) and optional bank account
        $request->validate([
            'payment_method' => 'required|in:bank,cash',
            'bank_account_id' => 'nullable|integer|exists:bank_accounts,id',
        ]);

        // Determine company and ensure liquidity based on chosen approval payment method
        $company = Company::find($loan->company_id);
        if (!$company) {
            return response()->json(['message' => 'Company not found'], 422);
        }

        // If approving by cash, ensure company has enough cash balance
        if ($request->input('payment_method') === 'cash') {
            if ((float)$company->cash_balance < (float)$loan->principal_amount) {
                return response()->json([
                    'message' => 'Insufficient cash balance. Loan approval requires available cash.',
                    'required' => $loan->principal_amount,
                    'available' => $company->cash_balance,
                ], 422);
            }
        }

        // If approving by bank, ensure selected bank account has sufficient funds
        if ($request->input('payment_method') === 'bank') {
            $approvalBankAccountId = $request->input('bank_account_id') ?? $loan->bank_account_id;
            if (!$approvalBankAccountId) {
                return response()->json(['message' => 'Bank account is required when approving by bank'], 422);
            }
            $bank = \App\Models\BankAccount::where('id', $approvalBankAccountId)
                ->where('company_id', $loan->company_id)
                ->first();
            if (!$bank) {
                return response()->json(['message' => 'Invalid bank account for this company'], 422);
            }
            if ((float)$bank->current_balance < (float)$loan->principal_amount) {
                return response()->json([
                    'message' => 'Insufficient funds in selected bank account',
                    'required' => $loan->principal_amount,
                    'available' => $bank->current_balance,
                    'bank_account' => $bank->bank_name . ' - ' . $bank->account_number
                ], 422);
            }
        }

        $interest      = $loan->interest_rate;
        $totalInterest = round($loan->principal_amount * ($interest / 100 / 12) * $loan->term_months, 2);
        $totalRepayable= round($loan->principal_amount + $totalInterest, 2);

        // Deduct principal from company net profit (recalculate/get current month financials)
        $mfin = MonthlyCompanyFinancial::currentMonth($loan->company_id);
        // If we got an Eloquent model, update and save; otherwise persist via updateOrCreate
        if (is_object($mfin) && method_exists($mfin, 'save')) {
            $mfin->profit_swept = max(0, (float)$mfin->profit_swept + (float)$loan->principal_amount);
            $mfin->save();
        } else {
            try {
                $yearMonth = now()->format('Y-m');
                MonthlyCompanyFinancial::updateOrCreate(
                    ['company_id' => $loan->company_id, 'year_month' => $yearMonth],
                    ['profit_swept' => max(0, (float)($mfin->profit_swept ?? 0) + (float)$loan->principal_amount)]
                );
            } catch (\Exception $e) {
                // swallowing error to avoid breaking approval flow if financial table isn't available
            }
        }

        // Persist chosen payment method on the loan (approval-time choice overrides request-time choice)
        $approvalPaymentMethod = $request->input('payment_method');
        $approvalBankAccountId = $request->input('bank_account_id') ?? null;

        // If bank selected, ensure bank account belongs to the company
        if ($approvalPaymentMethod === 'bank') {
            if (!$approvalBankAccountId) {
                return response()->json(['message' => 'Bank account is required when approving by bank'], 422);
            }
            $acct = \App\Models\BankAccount::where('id', $approvalBankAccountId)
                ->where('company_id', $loan->company_id)
                ->first();
            if (!$acct) {
                return response()->json(['message' => 'Invalid bank account for this company'], 422);
            }
        }

        $loan->update([
            'status'              => 'approved',
            'approved_by'         => $user->id,
            'outstanding_balance' => $totalRepayable,
            'start_date'          => now()->toDateString(),
            'payment_method'      => $approvalPaymentMethod,
            'bank_account_id'     => $approvalPaymentMethod === 'bank' ? $approvalBankAccountId : null,
        ]);

        $this->generateDeductionSchedule($loan);

        // Before creating the invoice, ensure the company has sufficient funds and perform the deduction
        $company = Company::find($loan->company_id);
        if (!$company) {
            return response()->json(['message' => 'Company not found'], 422);
        }

        if ($loan->payment_method === 'cash') {
            if ((float)$company->cash_balance < (float)$loan->principal_amount) {
                return response()->json([
                    'message' => 'Insufficient cash balance for this disbursement',
                    'required' => $loan->principal_amount,
                    'available' => $company->cash_balance
                ], 422);
            }
            $company->decrement('cash_balance', $loan->principal_amount);
        } elseif ($loan->payment_method === 'bank') {
            $bank = $loan->bankAccount;
            if (!$bank) {
                return response()->json(['message' => 'Bank account not found for this loan'], 422);
            }
            if ((float)$bank->current_balance < (float)$loan->principal_amount) {
                return response()->json([
                    'message' => 'Insufficient funds in selected bank account',
                    'required' => $loan->principal_amount,
                    'available' => $bank->current_balance,
                    'bank_account' => $bank->bank_name . ' - ' . $bank->account_number
                ], 422);
            }
            $bank->debit($loan->principal_amount);
        }

        // Create invoice record for staff loan (money going out)
        $invoice = \App\Models\Invoice::create([
            'invoice_number'  => 'SLN-' . date('Y') . '-' . strtoupper(substr(uniqid(), -6)),
            'company_id'      => $loan->company_id,
            'bank_account_id' => $loan->bank_account_id,
            'type'            => 'payable',
            'client_name'     => $loan->employee->first_name . ' ' . $loan->employee->last_name,
            'client_email'    => $loan->employee->email,
            'invoice_date'    => now()->toDateString(),
            'due_date'        => now()->addMonths($loan->term_months)->toDateString(),
            'subtotal'        => $loan->principal_amount,
            'tax_amount'      => 0,
            'discount_amount' => 0,
            'total_amount'    => $loan->principal_amount,
            'paid_amount'     => $loan->principal_amount,
            'balance'         => 0,
            'status'          => 'paid',
            'notes'           => 'Staff Loan - ' . $loan->loan_number . ' - ' . $loan->purpose,
            'terms'           => 'Staff loan repayment terms',
            'payment_method'  => $loan->payment_method,
            'cash_amount'     => $loan->payment_method === 'cash' ? $loan->principal_amount : 0,
        ]);

        // Add line item
        $invoice->items()->create([
            'description' => 'Staff Loan Disbursement - ' . $loan->purpose,
            'quantity'    => 1,
            'unit_price'  => $loan->principal_amount,
            'total_price' => $loan->principal_amount,
        ]);

        return response()->json([
            'data'    => $loan->fresh()->load(['employee', 'deductions']),
            'message' => 'Staff loan approved — deduction schedule generated',
        ]);
    }

    public function reject(Request $request, $id)
    {
        $user = $request->user();
        if (!$user->hasRole('Super Admin') && !$user->hasRole('Company Admin') && !$user->hasRole('Finance Manager')) {
            return response()->json(['message' => 'Unauthorized'], 403);
        }

        $validated = $request->validate(['reason' => 'required|string|max:500']);
        $loan = StaffLoan::findOrFail($id);

        if ($loan->status !== 'pending') {
            return response()->json(['message' => 'Loan is not pending'], 400);
        }

        $loan->update(['status' => 'rejected', 'notes' => ($loan->notes ? $loan->notes . ' | ' : '') . 'Rejected: ' . $validated['reason']]);

        return response()->json(['message' => 'Loan rejected']);
    }

    public function deductions($id)
    {
        $loan = StaffLoan::with('employee')->findOrFail($id);
        $deductions = StaffLoanDeduction::where('staff_loan_id', $id)
            ->orderBy('month_number')
            ->get()
            ->map(function ($d) use ($loan) {
                if ($d->status === 'scheduled' && $d->due_date && now()->gt($d->due_date)) {
                    $d->status = 'missed';
                }
                return $d;
            });

        return response()->json(['data' => $deductions, 'loan' => $loan]);
    }

    public function markDeducted(Request $request, $deductionId)
    {
        $user      = $request->user();
        $deduction = StaffLoanDeduction::findOrFail($deductionId);
        $loan      = StaffLoan::findOrFail($deduction->staff_loan_id);

        if (!$user->hasRole('Super Admin') && !$user->hasRole('Company Admin') && !$user->hasRole('Finance Manager')) {
            return response()->json(['message' => 'Unauthorized'], 403);
        }
        if ($deduction->status !== 'scheduled') {
            return response()->json(['message' => 'Already processed'], 400);
        }

        // Validate payment method and optional bank account for the repayment
        $validated = $request->validate([
            'payment_method'  => 'required|in:cash,bank',
            'bank_account_id' => 'nullable|integer|exists:bank_accounts,id',
        ]);

        $paymentMethod = $validated['payment_method'];
        $bankAccountId = $validated['bank_account_id'] ?? null;

        // If bank payment, ensure bank account belongs to this company
        $bank = null;
        if ($paymentMethod === 'bank') {
            if (!$bankAccountId) {
                return response()->json(['message' => 'Bank account is required when payment method is bank'], 422);
            }
            $bank = \App\Models\BankAccount::where('id', $bankAccountId)
                ->where('company_id', $loan->company_id)
                ->first();
            if (!$bank) {
                return response()->json(['message' => 'Invalid bank account for this company'], 422);
            }
        }

        // Mark deduction and update loan balance
        $deduction->update(['status' => 'deducted', 'deducted_on' => now()->toDateString()]);

        $newBalance = max(0, $loan->outstanding_balance - $deduction->deduction_amount);
        $loan->update([
            'outstanding_balance' => $newBalance,
            'status'              => $newBalance <= 0 ? 'paid_off' : $loan->status,
        ]);

        // Create invoice record for staff loan repayment (money coming in)
        $invoiceData = [
            'invoice_number'  => 'SLR-' . date('Y') . '-' . strtoupper(substr(uniqid(), -6)),
            'company_id'      => $loan->company_id,
            'type'            => 'receivable',
            'client_name'     => $loan->employee->first_name . ' ' . $loan->employee->last_name,
            'client_email'    => $loan->employee->email,
            'invoice_date'    => now()->toDateString(),
            'due_date'        => now()->toDateString(),
            'subtotal'        => $deduction->deduction_amount,
            'tax_amount'      => 0,
            'discount_amount' => 0,
            'total_amount'    => $deduction->deduction_amount,
            'paid_amount'     => $deduction->deduction_amount,
            'balance'         => 0,
            'status'          => 'paid',
            'notes'           => 'Staff Loan Repayment - ' . $loan->loan_number . ' - Month ' . $deduction->month_number,
            'terms'           => 'Staff loan repayment',
            'payment_method'  => $paymentMethod,
            'cash_amount'     => $paymentMethod === 'cash' ? $deduction->deduction_amount : 0,
            'bank_account_id' => $paymentMethod === 'bank' ? $bankAccountId : null,
        ];

        $invoice = \App\Models\Invoice::create($invoiceData);

        // Add line item
        $invoice->items()->create([
            'description' => 'Staff Loan Repayment - Month ' . $deduction->month_number,
            'quantity'    => 1,
            'unit_price'  => $deduction->deduction_amount,
            'total_price' => $deduction->deduction_amount,
        ]);

        // Credit the company's cash or bank account accordingly
        $company = Company::find($loan->company_id);
        if ($paymentMethod === 'cash') {
            $company->increment('cash_balance', $deduction->deduction_amount);
        } else {
            // credit the selected bank account
            try {
                $bank->credit($deduction->deduction_amount);
            } catch (\Exception $e) {
                // If credit fails, still return success for deduction but log/ignore the credit failure
            }
        }

        // Recalculate monthly financials to include the newly marked deduction in revenue
        MonthlyCompanyFinancial::recalculateMonth($loan->company_id, now()->format('Y-m'));

        return response()->json(['message' => 'Deduction recorded — balance updated', 'outstanding' => $newBalance]);
    }

    private function generateDeductionSchedule(StaffLoan $loan): void
    {
        $employee = $loan->employee;
        
        // Calculate total interest and total repayment
        $totalInterest = round($loan->principal_amount * ($loan->interest_rate / 100 / 12) * $loan->term_months, 2);
        $totalRepayable = round($loan->principal_amount + $totalInterest, 2);
        
        // Calculate equal monthly payment (principal + interest) divided by term months
        $monthlyPayment = round($totalRepayable / $loan->term_months, 2);
        
        // Calculate portions for each month
        $monthlyPrincipal = round($loan->principal_amount / $loan->term_months, 2);
        $monthlyInterest = round($totalInterest / $loan->term_months, 2);

        // Validate deduction amount makes sense
        if ($loan->deduction_type === 'percentage') {
            $salary = (float)($employee->salary ?? 0);
            $calculatedDeduction = $salary > 0 ? round($salary * ($loan->deduction_value / 100), 2) : $monthlyPayment;
            
            // Ensure percentage deduction covers the monthly payment
            if ($calculatedDeduction < $monthlyPayment) {
                // Use the calculated monthly payment instead
                $monthlyDeduction = $monthlyPayment;
            } else {
                $monthlyDeduction = $calculatedDeduction;
            }
        } else {
            // Fixed amount - ensure it covers the monthly payment
            $monthlyDeduction = (float)$loan->deduction_value >= $monthlyPayment 
                ? (float)$loan->deduction_value 
                : $monthlyPayment;
        }

        $start = $loan->start_date ?? now();
        for ($i = 1; $i <= $loan->term_months; $i++) {
            StaffLoanDeduction::create([
                'staff_loan_id'    => $loan->id,
                'employee_id'      => $loan->employee_id,
                'month_number'     => $i,
                'due_date'         => Carbon::parse($start)->addMonths($i)->toDateString(),
                'deduction_amount' => $monthlyDeduction,
                'principal_portion'=> $monthlyPrincipal,
                'interest_portion' => $monthlyInterest,
                'status'           => 'scheduled',
            ]);
        }
    }
}
