<?php

namespace App\Controllers;

use App\Core\Controller;
use App\Helpers\Auth;
use App\Models\Loan;
use App\Models\LoanType;
use App\Models\LoanRepayment;
use App\Models\AuditLog;
use App\Models\Setting;
use App\Helpers\Mailer;
use App\Helpers\PaymentGateway;
use App\Helpers\Url;
use App\Models\User;

class LoanController extends Controller
{
    public function __construct()
    {
        parent::__construct();
        Auth::requireLogin();
    }

    private function requireAdmin()
    {
        if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'admin') {
            $_SESSION['error'] = 'Access denied';
            $this->redirect('/dashboard');
        }
    }

    // Cron: process due repayments, send reminders and initiate payment links when possible
    public function processDeductions()
    {
        $settingModel = new Setting();
        $enabled = $settingModel->getValue('loan_auto_deduction_enabled', 'false');
        if ($enabled !== 'true') {
            echo "Loan auto deduction disabled\n";
            return;
        }

        // First, mark overdue repayments
        $repaymentModel = new LoanRepayment();
        $repaymentModel->markOverdue();

        // Find due repayments (pending or partial) due today or earlier
        $stmt = $this->db->prepare(
            "SELECT lr.*, l.user_id, l.id AS loan_id, u.email, u.first_name, u.last_name, u.name FROM loan_repayments lr JOIN loans l ON lr.loan_id = l.id JOIN users u ON l.user_id = u.id WHERE lr.status IN ('pending','partial') AND lr.due_date <= CURDATE()"
        );
        $stmt->execute();
        $due = $stmt->fetchAll(\PDO::FETCH_OBJ);

        if (empty($due)) {
            echo "No due repayments to process\n";
            return;
        }

        $sent = 0;
        foreach ($due as $d) {
            $memberName = $d->first_name ? trim($d->first_name . ' ' . $d->last_name) : ($d->name ?? $d->email);
            $amount = (float)$d->amount;
            $subject = "Loan Repayment Due - GHS " . number_format($amount, 2);

            $body = "<p>Dear {$memberName},</p>";
            $body .= "<p>Your loan repayment of <strong>GHS " . number_format($amount, 2) . "</strong> for installment #{$d->installment_number} is due on <strong>{$d->due_date}</strong>.</p>";

            // Try to initialize an online payment link if a gateway is configured
            if (PaymentGateway::isEnabled()) {
                $reference = 'loan_lr_' . $d->id . '_' . time();
                $callback = Url::to('/payment/callback');
                $init = PaymentGateway::initPaystackPayment($d->email, $amount, $reference, $callback);
                if (!empty($init['success']) && !empty($init['authorization_url'])) {
                    $body .= "<p>Please pay using the link below:<br><a href='" . $init['authorization_url'] . "'>Pay Now</a></p>";
                    // Save payment request for admin tracking
                    $pr = new \App\Models\PaymentRequest();
                    $pr->createRequest([
                        'user_id' => $d->user_id,
                        'loan_repayment_id' => $d->id,
                        'gateway' => 'paystack',
                        'reference' => $reference,
                        'authorization_url' => $init['authorization_url'],
                        'amount' => $amount,
                        'status' => 'pending',
                    ]);
                } else {
                    $body .= "<p>Please log in to the portal to pay your loan repayment.</p>";
                }
            } else {
                $body .= "<p>Please log in to the portal to pay your loan repayment.</p>";
            }

            Mailer::send($d->email, $subject, $body);

            // create internal message
            $this->db->prepare("INSERT INTO messages (sender_id, subject, body) VALUES (1, ?, ?)")->execute([$subject, strip_tags($body)]);
            $msgId = $this->db->lastInsertId();
            $this->db->prepare("INSERT INTO message_recipients (message_id, recipient_id) VALUES (?, ?)")->execute([$msgId, $d->user_id]);

            $audit = new AuditLog();
            $audit->log(0, 'loan.cron.notify', 'loan_repayments', $d->id, "Notified user {$d->user_id} about due repayment");

            $sent++;
        }

        echo "Processed {$sent} due repayments\n";
    }

    // ─── Loan Types (Admin) ───────────────────────────

    public function types()
    {
        $this->requireAdmin();
        $model = new LoanType();
        $types = $model->all();
        $this->view('loans/types', ['types' => $types]);
    }

    public function createType()
    {
        $this->requireAdmin();
        $this->view('loans/type_form', ['type' => null]);
    }

    public function storeType()
    {
        $this->requireAdmin();
        $model = new LoanType();
        $model->create([
            'name' => $_POST['name'] ?? '',
            'description' => $_POST['description'] ?? '',
            'interest_rate' => (float)($_POST['interest_rate'] ?? 0),
            'min_amount' => (float)($_POST['min_amount'] ?? 0),
            'max_amount' => (float)($_POST['max_amount'] ?? 0),
            'min_duration_months' => (int)($_POST['min_duration_months'] ?? 1),
            'max_duration_months' => (int)($_POST['max_duration_months'] ?? 12),
            'is_active' => isset($_POST['is_active']) ? 1 : 0,
        ]);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan_type.create', 'loan_types', $this->db->lastInsertId(), "Created loan type: {$_POST['name']}");

        $_SESSION['success'] = 'Loan type created';
        $this->redirect('/loans/types');
    }

    public function editType($params)
    {
        $this->requireAdmin();
        $model = new LoanType();
        $type = $model->find($params['id'] ?? 0);
        if (!$type) { $_SESSION['error'] = 'Loan type not found'; $this->redirect('/loans/types'); }
        $this->view('loans/type_form', ['type' => $type]);
    }

    public function updateType($params)
    {
        $this->requireAdmin();
        $model = new LoanType();
        $type = $model->find($params['id'] ?? 0);
        if (!$type) { $_SESSION['error'] = 'Loan type not found'; $this->redirect('/loans/types'); }

        $model->update($type->id, [
            'name' => $_POST['name'] ?? $type->name,
            'description' => $_POST['description'] ?? $type->description,
            'interest_rate' => (float)($_POST['interest_rate'] ?? $type->interest_rate),
            'min_amount' => (float)($_POST['min_amount'] ?? $type->min_amount),
            'max_amount' => (float)($_POST['max_amount'] ?? $type->max_amount),
            'min_duration_months' => (int)($_POST['min_duration_months'] ?? $type->min_duration_months),
            'max_duration_months' => (int)($_POST['max_duration_months'] ?? $type->max_duration_months),
            'is_active' => isset($_POST['is_active']) ? 1 : 0,
        ]);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan_type.update', 'loan_types', $type->id, "Updated loan type: {$_POST['name']}");

        $_SESSION['success'] = 'Loan type updated';
        $this->redirect('/loans/types');
    }

    public function deleteType($params)
    {
        $this->requireAdmin();
        $model = new LoanType();
        $type = $model->find($params['id'] ?? 0);
        if ($type) {
            $audit = new AuditLog();
            $audit->log(Auth::id(), 'loan_type.delete', 'loan_types', $type->id, "Deleted loan type: {$type->name}");
            $model->delete($type->id);
            $_SESSION['success'] = 'Loan type deleted';
        }
        $this->redirect('/loans/types');
    }

    // ─── Loan Applications (Member) ───────────────────

    public function apply()
    {
        $typeModel = new LoanType();
        $types = $typeModel->getActive();
        $this->view('loans/apply', ['types' => $types]);
    }

    public function store()
    {
        $amount = (float)($_POST['amount'] ?? 0);
        $typeId = (int)($_POST['loan_type_id'] ?? 0);
        $duration = (int)($_POST['duration_months'] ?? 1);
        $frequency = $_POST['repayment_frequency'] ?? 'monthly';
        $purpose = trim($_POST['purpose'] ?? '');

        $typeModel = new LoanType();
        $loanType = $typeModel->find($typeId);
        if (!$loanType || !$loanType->is_active) {
            $_SESSION['error'] = 'Invalid loan type';
            $this->redirect('/loans/apply');
        }

        if ($amount < $loanType->min_amount || $amount > $loanType->max_amount) {
            $_SESSION['error'] = "Amount must be between GHS {$loanType->min_amount} and GHS {$loanType->max_amount}";
            $this->redirect('/loans/apply');
        }
        if ($duration < $loanType->min_duration_months || $duration > $loanType->max_duration_months) {
            $_SESSION['error'] = "Duration must be between {$loanType->min_duration_months} and {$loanType->max_duration_months} months";
            $this->redirect('/loans/apply');
        }

        $interestRate = (float)$loanType->interest_rate;
        $totalPayable = $amount + ($amount * $interestRate / 100);
        $installmentAmount = \App\Models\Loan::calcInstallmentAmount($totalPayable, $duration, $frequency);

        $loanModel = new \App\Models\Loan();
        $loanModel->create([
            'user_id' => Auth::id(),
            'loan_type_id' => $typeId,
            'amount' => $amount,
            'interest_rate' => $interestRate,
            'duration_months' => $duration,
            'repayment_frequency' => $frequency,
            'total_payable' => $totalPayable,
            'monthly_payment' => $installmentAmount,
            'purpose' => $purpose,
            'status' => 'pending',
        ]);

        $_SESSION['success'] = 'Loan application submitted successfully. Awaiting approval.';
        $this->redirect('/loans/my');
    }

    // ─── Admin Loan Creation ──────────────────────────

    public function adminCreate()
    {
        $this->requireAdmin();
        $typeModel = new LoanType();
        $types = $typeModel->getActive();
        $stmt = $this->db->query("SELECT id, name, first_name, last_name, email FROM users WHERE approval_status = 'approved' AND status = 'active' ORDER BY first_name ASC");
        $members = $stmt->fetchAll(\PDO::FETCH_OBJ);
        $this->view('loans/create', ['types' => $types, 'members' => $members]);
    }

    public function adminStore()
    {
        $this->requireAdmin();

        $userId = (int)($_POST['user_id'] ?? 0);
        $typeId = (int)($_POST['loan_type_id'] ?? 0);
        $amount = (float)($_POST['amount'] ?? 0);
        $interestRate = (float)($_POST['interest_rate'] ?? 0);
        $duration = (int)($_POST['duration_months'] ?? 1);
        $frequency = $_POST['repayment_frequency'] ?? 'monthly';
        $purpose = trim($_POST['purpose'] ?? '');
        $notes = trim($_POST['notes'] ?? '');
        $status = $_POST['status'] ?? 'pending';

        if (!$userId || !$typeId || $amount <= 0) {
            $_SESSION['error'] = 'Please fill in all required fields';
            $this->redirect('/loans/create');
        }

        $userModel = new \App\Models\User();
        $member = $userModel->find($userId);
        if (!$member) {
            $_SESSION['error'] = 'Member not found';
            $this->redirect('/loans/create');
        }

        $totalPayable = $amount + ($amount * $interestRate / 100);
        $installmentAmount = \App\Models\Loan::calcInstallmentAmount($totalPayable, $duration, $frequency);

        $loanModel = new \App\Models\Loan();
        $loanId = $loanModel->create([
            'user_id' => $userId,
            'loan_type_id' => $typeId,
            'amount' => $amount,
            'interest_rate' => $interestRate,
            'duration_months' => $duration,
            'repayment_frequency' => $frequency,
            'total_payable' => $totalPayable,
            'monthly_payment' => $installmentAmount,
            'purpose' => $purpose,
            'notes' => $notes ?? '',
            'status' => $status,
            'application_date' => date('Y-m-d H:i:s'),
        ]);

        // If approved directly, generate schedule immediately
        if ($status === 'approved') {
            $loanModel->update($loanId, [
                'approval_date' => date('Y-m-d H:i:s'),
                'approved_by' => Auth::id(),
            ]);
            $loanModel->generateRepaymentSchedule($loanId);
        }

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan.create', 'loans', $loanId, "Admin created loan #{$loanId} for user #{$userId}");

        $_SESSION['success'] = 'Loan created successfully';
        $this->redirect('/loans/' . $loanId);
    }

    public function searchMembersJson()
    {
        $this->requireAdmin();
        $q = $_GET['q'] ?? '';
        if (strlen($q) < 1) {
            $this->json([]);
        }
        $userModel = new \App\Models\User();
        $like = "%{$q}%";
        $stmt = $this->db->prepare("
            SELECT id, name, first_name, last_name, email
            FROM users
            WHERE approval_status = 'approved' AND status = 'active'
            AND (first_name LIKE ? OR last_name LIKE ? OR name LIKE ? OR email LIKE ?)
            LIMIT 20
        ");
        $stmt->execute([$like, $like, $like, $like]);
        $this->json($stmt->fetchAll(\PDO::FETCH_ASSOC));
    }

    // ─── My Loans (Member) ────────────────────────────

    public function my()
    {
        $loanModel = new Loan();
        $repaymentModel = new LoanRepayment();
        $loans = $loanModel->getByUser(Auth::id());
        $upcoming = $repaymentModel->getUpcomingByUser(Auth::id());
        $overdue = $repaymentModel->getOverdueByUser(Auth::id());
        $summary = $loanModel->getSummary(Auth::id());

        $this->view('loans/my', [
            'loans' => $loans,
            'upcoming' => $upcoming,
            'overdue' => $overdue,
            'summary' => $summary,
        ]);
    }

    // ─── All Loans (Admin) ────────────────────────────

    public function index()
    {
        $this->requireAdmin();
        $page = max(1, (int)($_GET['page'] ?? 1));
        $status = $_GET['status'] ?? null;
        $search = $_GET['search'] ?? '';

        $loanModel = new Loan();
        $result = $loanModel->getAllWithDetails($page, 20, $status, $search);

        $this->view('loans/index', [
            'loans' => $result->data,
            'pagination' => $result,
            'currentStatus' => $status,
            'search' => $search,
        ]);
    }

    // ─── View Single Loan (Admin + Member) ────────────

    public function detail($params)
    {
        $loanId = $params['id'] ?? 0;
        $loanModel = new Loan();
        $loan = $loanModel->getWithDetails($loanId);
        if (!$loan) {
            $_SESSION['error'] = 'Loan not found';
            $this->redirect('/loans/my');
        }

        $isAdmin = isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin';
        if ($loan->user_id != Auth::id() && !$isAdmin) {
            $_SESSION['error'] = 'Access denied';
            $this->redirect('/loans/my');
        }

        $repaymentModel = new LoanRepayment();
        $repayments = $repaymentModel->getByLoan($loanId);
        $totals = $repaymentModel->getTotalsByLoan($loanId);

        $this->view('loans/view', [
            'loan' => $loan,
            'repayments' => $repayments,
            'totals' => $totals,
            'isAdmin' => $isAdmin,
        ]);
    }

    // ─── Approve / Reject (Admin) ─────────────────────

    public function approve($params)
    {
        $this->requireAdmin();
        $loanId = $params['id'] ?? 0;
        $loanModel = new Loan();
        $loan = $loanModel->find($loanId);
        if (!$loan || $loan->status !== 'pending') {
            $_SESSION['error'] = 'Invalid loan';
            $this->redirect('/loans');
        }

        $loanModel->update($loanId, [
            'status' => 'approved',
            'approval_date' => date('Y-m-d H:i:s'),
            'approved_by' => Auth::id(),
            'notes' => $_POST['notes'] ?? $loan->notes,
        ]);

        $loanModel->generateRepaymentSchedule($loanId);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan.approve', 'loans', $loanId, "Approved loan #{$loanId}");

        $_SESSION['success'] = 'Loan approved and repayment schedule generated';
        $this->redirect('/loans/' . $loanId);
    }

    public function reject($params)
    {
        $this->requireAdmin();
        $loanId = $params['id'] ?? 0;
        $loanModel = new Loan();
        $loan = $loanModel->find($loanId);
        if (!$loan || $loan->status !== 'pending') {
            $_SESSION['error'] = 'Invalid loan';
            $this->redirect('/loans');
        }

        $loanModel->update($loanId, [
            'status' => 'rejected',
            'approval_date' => date('Y-m-d H:i:s'),
            'approved_by' => Auth::id(),
            'notes' => $_POST['notes'] ?? $loan->notes,
        ]);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan.reject', 'loans', $loanId, "Rejected loan #{$loanId}");

        $_SESSION['success'] = 'Loan rejected';
        $this->redirect('/loans/' . $loanId);
    }

    public function disburse($params)
    {
        $this->requireAdmin();
        $loanId = $params['id'] ?? 0;
        $loanModel = new Loan();
        $loan = $loanModel->find($loanId);
        if (!$loan || $loan->status !== 'approved') {
            $_SESSION['error'] = 'Loan must be approved first';
            $this->redirect('/loans');
        }

        $loanModel->update($loanId, [
            'status' => 'disbursed',
            'disbursement_date' => date('Y-m-d H:i:s'),
        ]);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan.disburse', 'loans', $loanId, "Disbursed loan #{$loanId}");

        $_SESSION['success'] = 'Loan marked as disbursed';
        $this->redirect('/loans/' . $loanId);
    }

    // ─── Record Repayment (Admin) ─────────────────────

    public function recordRepayment($params)
    {
        $this->requireAdmin();
        $repaymentId = $params['id'] ?? 0;
        $amount = (float)($_POST['amount'] ?? 0);
        $method = $_POST['payment_method'] ?? 'cash';
        $ref = $_POST['transaction_ref'] ?? null;

        if ($amount <= 0) {
            $_SESSION['error'] = 'Invalid payment amount';
            $this->redirect('/loans/' . ($_POST['loan_id'] ?? 0));
        }

        $repaymentModel = new LoanRepayment();
        $repayment = $repaymentModel->recordPayment($repaymentId, $amount, $method, $ref);

        if ($repayment) {
            $loanModel = new Loan();
            $totals = $repaymentModel->getTotalsByLoan($repayment->loan_id);
            $loan = $loanModel->find($repayment->loan_id);

            if ($totals->total_paid >= $totals->total_due) {
                $loanModel->update($repayment->loan_id, ['status' => 'completed']);
            }

            $audit = new AuditLog();
            $audit->log(Auth::id(), 'loan.repayment', 'loan_repayments', $repaymentId, "Recorded repayment GHS {$amount} for loan #{$repayment->loan_id}");

            $_SESSION['success'] = 'Repayment recorded';
        } else {
            $_SESSION['error'] = 'Could not record payment';
        }

        $this->redirect('/loans/' . ($repayment->loan_id ?? $_POST['loan_id']));
    }

    public function markCompleted($params)
    {
        $this->requireAdmin();
        $loanId = $params['id'] ?? 0;
        $loanModel = new Loan();
        $loanModel->update($loanId, ['status' => 'completed']);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan.complete', 'loans', $loanId, "Manually completed loan #{$loanId}");

        $_SESSION['success'] = 'Loan marked as completed';
        $this->redirect('/loans/' . $loanId);
    }

    public function markDefaulted($params)
    {
        $this->requireAdmin();
        $loanId = $params['id'] ?? 0;
        $loanModel = new Loan();
        $loanModel->update($loanId, ['status' => 'defaulted']);

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'loan.default', 'loans', $loanId, "Marked loan #{$loanId} as defaulted");

        $_SESSION['success'] = 'Loan marked as defaulted';
        $this->redirect('/loans/' . $loanId);
    }
}
