<?php
//borrow loan charges handling from the above main admin script and update this member self application script  
// member self loan-application-api.php - REST API for loan applications with proper charge implementation

// ============================================
// INITIALIZATION & CONFIGURATION
// ============================================

// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

// Load CORS configuration FIRST
require_once __DIR__ . '/includes/cors.php';
configureCors();

// Load environment variables
require_once __DIR__ . '/includes/config_loader.php';

// Load dependencies
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/auth_functions.php';
require_once __DIR__ . '/includes/sms_helper.php';
require_once __DIR__ . '/includes/email_helper.php';
require_once __DIR__ . '/auth/AuditLogger.php';
require_once __DIR__ . '/auth/JWTManager.php';

// Load PHPMailer for advanced email
require_once __DIR__ . '/vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

// Set response headers
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
ini_set('log_errors', 1);
error_reporting(E_ALL & ~E_NOTICE);

// Security headers
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');

// Handle preflight OPTIONS request
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit();
}

// ============================================
// AUTHENTICATION
// ============================================

// Check if user is logged in
if (!isset($_SESSION['member_id'])) {
    http_response_code(401);
    echo json_encode([
        'success' => false,
        'error' => 'Authentication required',
        'code' => 'UNAUTHORIZED'
    ]);
    exit();
}

$memberId = (int)$_SESSION['member_id'];
$userRole = $_SESSION['role'] ?? 'member';

// ============================================
// GLOBAL VARIABLES
// ============================================

$method = $_SERVER['REQUEST_METHOD'];
$action = $_GET['action'] ?? null;

// ============================================
// NOTIFICATION FUNCTIONS (Borrowed from member-registration-api)
// ============================================

/**
 * Get SMTP settings from JSON file or database
 */
function getLoanSmtpSettings($pdo) {
    $jsonFilePath = __DIR__ . '/smtp_settings_data.json';
    
    if (file_exists($jsonFilePath)) {
        $json = file_get_contents($jsonFilePath);
        $settings = json_decode($json, true);
        
        if (json_last_error() === JSON_ERROR_NONE && !empty($settings['smtp_host'])) {
            return $settings;
        }
    }
    
    try {
        $stmt = $pdo->query("SELECT * FROM email_settings LIMIT 1");
        $dbSettings = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($dbSettings) {
            return [
                'smtp_host' => $dbSettings['smtp_host'],
                'smtp_username' => $dbSettings['smtp_username'],
                'smtp_password' => $dbSettings['smtp_password'],
                'smtp_port' => $dbSettings['smtp_port'],
                'smtp_encryption' => $dbSettings['smtp_encryption'],
                'from_name' => $dbSettings['from_name']
            ];
        }
    } catch (Exception $e) {
        error_log("Database SMTP settings failed: " . $e->getMessage());
    }
    
    return [
        'smtp_host' => 'smtp.gmail.com',
        'smtp_username' => '',
        'smtp_password' => '',
        'smtp_port' => 587,
        'smtp_encryption' => 'tls',
        'from_name' => 'Loan System'
    ];
}

/**
 * Send email notification using PHPMailer
 */
function sendLoanEmail($recipientEmail, $subject, $htmlBody, $textBody = null) {
    try {
        global $pdo;
        $smtpSettings = getLoanSmtpSettings($pdo);
        
        if (empty($smtpSettings['smtp_username']) || empty($smtpSettings['smtp_password'])) {
            error_log("SMTP not configured: Missing credentials");
            return false;
        }
        
        $mail = new PHPMailer(true);
        
        $mail->isSMTP();
        $mail->Host = $smtpSettings['smtp_host'];
        $mail->SMTPAuth = true;
        $mail->Username = $smtpSettings['smtp_username'];
        $mail->Password = $smtpSettings['smtp_password'];
        $mail->SMTPSecure = $smtpSettings['smtp_encryption'];
        $mail->Port = $smtpSettings['smtp_port'];
        
        $mail->SMTPOptions = array(
            'ssl' => array(
                'verify_peer' => false,
                'verify_peer_name' => false,
                'allow_self_signed' => true
            )
        );
        
        $mail->setFrom($smtpSettings['smtp_username'], $smtpSettings['from_name'] ?? 'Loan System');
        $mail->addAddress($recipientEmail);
        
        $mail->isHTML(true);
        $mail->Subject = $subject;
        $mail->Body = $htmlBody;
        $mail->AltBody = $textBody ?? strip_tags($htmlBody);
        
        return $mail->send();
        
    } catch (Exception $e) {
        error_log("Loan Email Failed: " . $e->getMessage());
        return false;
    }
}

/**
 * Send SMS notification
 */
function sendLoanSMS($phoneNumber, $message) {
    try {
        if (empty($phoneNumber)) {
            error_log("No phone number provided for SMS");
            return false;
        }
        
        if (function_exists('sendSms')) {
            global $pdo;
            return sendSms($pdo, $phoneNumber, $message);
        } else {
            error_log("sendSms function not available");
            return false;
        }
    } catch (Exception $e) {
        error_log("Loan SMS Failed: " . $e->getMessage());
        return false;
    }
}

/**
 * Send loan application confirmation to loanee (applicant)
 */
function sendLoaneeApplicationNotification($pdo, $memberId, $loanData) {
    try {
        $db = new DatabaseWrapper($pdo);
        
        $member = $db->fetchOne("
            SELECT first_name, last_name, email_address, phone_number, communication_preference
            FROM members WHERE id = ?
        ", [$memberId]);
        
        if (!$member) {
            error_log("Member not found for notification: " . $memberId);
            return false;
        }
        
        $subject = "Loan Application Received - #{$loanData['loan_number']}";
        
        $htmlBody = "
            <html>
            <head>
                <style>
                    body { font-family: Arial, sans-serif; line-height: 1.6; }
                    .container { max-width: 600px; margin: 0 auto; padding: 20px; }
                    .header { background-color: #4CAF50; color: white; padding: 15px; text-align: center; border-radius: 5px; }
                    .content { padding: 20px; background-color: #f9f9f9; border-radius: 5px; margin-top: 15px; }
                    .amount { font-size: 24px; color: #4CAF50; font-weight: bold; }
                    .footer { margin-top: 20px; font-size: 12px; color: #666; text-align: center; }
                    table { width: 100%; border-collapse: collapse; }
                    td { padding: 8px; border-bottom: 1px solid #ddd; }
                    .label { font-weight: bold; width: 40%; }
                </style>
            </head>
            <body>
                <div class='container'>
                    <div class='header'>
                        <h2>Loan Application Received</h2>
                    </div>
                    <div class='content'>
                        <p>Dear {$member['first_name']} {$member['last_name']},</p>
                        <p>Thank you for submitting your loan application. We have received it and it is now being processed.</p>
                        
                        <h3>Application Details:</h3>
                        <table>
                            <tr><td class='label'>Loan Number:</td><td><strong>{$loanData['loan_number']}</strong></td></tr>
                            <tr><td class='label'>Loan Amount:</td><td class='amount'>Ksh " . number_format($loanData['loan_amount'], 2) . "</td></tr>
                            <tr><td class='label'>Loan Period:</td><td>{$loanData['loan_period']} months</td></tr>
                            <tr><td class='label'>Interest Rate:</td><td>{$loanData['interest_rate']}% p.a.</td></tr>
                            <tr><td class='label'>Total Charges:</td><td>Ksh " . number_format($loanData['total_charges'] ?? 0, 2) . "</td></tr>
                            <tr><td class='label'>Total Payable:</td><td>Ksh " . number_format(($loanData['loan_amount'] + ($loanData['total_charges'] ?? 0)), 2) . "</td></tr>
                            <tr><td class='label'>Self Guarantee:</td><td>Ksh " . number_format($loanData['self_guarantee_amount'] ?? 0, 2) . "</td></tr>
                            <tr><td class='label'>Guarantors Required:</td><td>" . ($loanData['guarantor_count'] ?? 0) . "</td></tr>
                            <tr><td class='label'>Application Date:</td><td>" . date('Y-m-d H:i:s') . "</td></tr>
                            <tr><td class='label'>Status:</td><td><span style='color: #ff9800;'>Pending Review</span></td></tr>
                        </table>
                        
                        <p><strong>Next Steps:</strong></p>
                        <ul>
                            <li>Our team will review your application within 2-3 business days</li>
                            <li>You can track the status of your application in your member portal</li>
                            <li>If guarantors are required, they will be notified to approve the request</li>
                            <li>Once approved, funds will be disbursed to your nominated account</li>
                        </ul>
                        
                        <p>If you have any questions, please contact our customer support team.</p>
                        <p>Thank you for choosing our services!</p>
                    </div>
                    <div class='footer'>
                        <p>This is an automated message, please do not reply to this email.</p>
                    </div>
                </div>
            </body>
            </html>
        ";
        
        $textBody = "Dear {$member['first_name']} {$member['last_name']},\n\n";
        $textBody .= "Thank you for submitting your loan application. We have received it and it is now being processed.\n\n";
        $textBody .= "Loan Number: {$loanData['loan_number']}\n";
        $textBody .= "Loan Amount: Ksh " . number_format($loanData['loan_amount'], 2) . "\n";
        $textBody .= "Loan Period: {$loanData['loan_period']} months\n";
        $textBody .= "Total Charges: Ksh " . number_format($loanData['total_charges'] ?? 0, 2) . "\n\n";
        $textBody .= "Next Steps:\n";
        $textBody .= "- Our team will review your application within 2-3 business days\n";
        $textBody .= "- You can track the status in your member portal\n";
        $textBody .= "- Once approved, funds will be disbursed\n\n";
        $textBody .= "Thank you for choosing our services!";
        
        $preference = $member['communication_preference'] ?? 'both';
        $emailSent = false;
        $smsSent = false;
        
        if (($preference === 'email' || $preference === 'both') && !empty($member['email_address'])) {
            $emailSent = sendLoanEmail($member['email_address'], $subject, $htmlBody, $textBody);
            error_log("Loanee notification email sent: " . ($emailSent ? 'Success' : 'Failed'));
        }
        
        if (($preference === 'sms' || $preference === 'both') && !empty($member['phone_number'])) {
            $smsMessage = "Dear {$member['first_name']}, loan application #{$loanData['loan_number']} for Ksh " . 
                         number_format($loanData['loan_amount'], 2) . " received. We'll process and notify you shortly.";
            $smsSent = sendLoanSMS($member['phone_number'], $smsMessage);
            error_log("Loanee notification SMS sent: " . ($smsSent ? 'Success' : 'Failed'));
        }
        
        return ($emailSent || $smsSent);
        
    } catch (Exception $e) {
        error_log("Send loanee notification error: " . $e->getMessage());
        return false;
    }
}

/**
 * Send guarantor request notification to guarantor (ONLY after successful loan creation)
 */
function sendGuarantorRequestNotification($pdo, $guarantorId, $loanData, $applicantName, $guaranteeAmount) {
    try {
        $db = new DatabaseWrapper($pdo);
        
        $guarantor = $db->fetchOne("
            SELECT first_name, last_name, email_address, phone_number, communication_preference
            FROM members WHERE id = ?
        ", [$guarantorId]);
        
        if (!$guarantor) {
            error_log("Guarantor not found for notification: " . $guarantorId);
            return false;
        }
        
        $subject = "Guarantor Request - Loan #{$loanData['loan_number']}";
        
        $htmlBody = "
            <html>
            <head>
                <style>
                    body { font-family: Arial, sans-serif; line-height: 1.6; }
                    .container { max-width: 600px; margin: 0 auto; padding: 20px; }
                    .header { background-color: #2196F3; color: white; padding: 15px; text-align: center; border-radius: 5px; }
                    .content { padding: 20px; background-color: #f9f9f9; border-radius: 5px; margin-top: 15px; }
                    .amount { font-size: 24px; color: #2196F3; font-weight: bold; }
                    .button { display: inline-block; padding: 10px 20px; margin: 10px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 5px; }
                    .button-decline { background-color: #f44336; }
                    .footer { margin-top: 20px; font-size: 12px; color: #666; text-align: center; }
                    table { width: 100%; border-collapse: collapse; }
                    td { padding: 8px; border-bottom: 1px solid #ddd; }
                    .label { font-weight: bold; width: 40%; }
                    .warning { background-color: #fff3cd; padding: 10px; border-left: 4px solid #ffc107; margin: 10px 0; }
                </style>
            </head>
            <body>
                <div class='container'>
                    <div class='header'>
                        <h2>Loan Guarantor Request</h2>
                    </div>
                    <div class='content'>
                        <p>Dear {$guarantor['first_name']} {$guarantor['last_name']},</p>
                        <p><strong>{$applicantName}</strong> has requested you to act as a guarantor for their loan application.</p>
                        
                        <h3>Loan Details:</h3>
                        <table>
                            <tr><td class='label'>Loan Number:</td><td><strong>{$loanData['loan_number']}</strong></td></tr>
                            <tr><td class='label'>Total Loan Amount:</td><td class='amount'>Ksh " . number_format($loanData['loan_amount'], 2) . "</td></tr>
                            <tr><td class='label'>Your Guarantee Amount:</td><td style='color: #f44336; font-weight: bold;'>Ksh " . number_format($guaranteeAmount, 2) . "</td></tr>
                            <tr><td class='label'>Loan Period:</td><td>{$loanData['loan_period']} months</td></tr>
                            <tr><td class='label'>Interest Rate:</td><td>{$loanData['interest_rate']}% p.a.</td></tr>
                            <tr><td class='label'>Monthly Installment:</td><td>Ksh " . number_format($loanData['monthly_installment'] ?? 0, 2) . "</td></tr>
                        </table>
                        
                        <div class='warning'>
                            <strong>⚠️ Your Obligation as a Guarantor:</strong><br>
                            - Your savings account will be used as collateral for this guarantee<br>
                            - The guaranteed amount (Ksh " . number_format($guaranteeAmount, 2) . ") will be locked until the loan is fully repaid<br>
                            - If the borrower defaults, you will be responsible for repaying this amount<br>
                            - You are required to respond to this request within 7 days<br>
                            - Failure to respond will result in automatic decline
                        </div>
                        
                        <p><strong>Please take action:</strong></p>
                        <p>
                            <a href='https://www.membersportal.creativewebsolutions.co.ke/guarantor-requests' style='display: inline-block; padding: 10px 20px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 5px; margin-right: 10px;'>View Request</a>
                        </p>
                        
                        <p>Please log in to your member portal to review and respond to this guarantor request.</p>
                        <p>Thank you for your support!</p>
                    </div>
                    <div class='footer'>
                        <p>This is an automated message, please do not reply to this email.</p>
                    </div>
                </div>
            </body>
            </html>
        ";
        
        // Enhanced SMS with complete details including obligation
        $smsMessage = "GUARANTOR REQUEST: {$applicantName} requests you to guarantee loan #{$loanData['loan_number']} of Ksh " . 
                     number_format($loanData['loan_amount'], 2) . " for {$loanData['loan_period']} months. " .
                     "Your guarantee amount: Ksh " . number_format($guaranteeAmount, 2) . ". " .
                     "This amount will be locked from your savings. " .
                     "Login to your portal to respond. T&C apply.";
        
        $preference = $guarantor['communication_preference'] ?? 'both';
        $emailSent = false;
        $smsSent = false;
        
        // Send email
        if (($preference === 'email' || $preference === 'both') && !empty($guarantor['email_address'])) {
            $emailSent = sendLoanEmail($guarantor['email_address'], $subject, $htmlBody);
            error_log("Guarantor request email sent to {$guarantor['email_address']}: " . ($emailSent ? 'Success' : 'Failed'));
        }
        
        // Send SMS
        if (($preference === 'sms' || $preference === 'both') && !empty($guarantor['phone_number'])) {
            $smsSent = sendLoanSMS($guarantor['phone_number'], $smsMessage);
            error_log("Guarantor request SMS sent to {$guarantor['phone_number']}: " . ($smsSent ? 'Success' : 'Failed'));
        }
        
        // Add internal notification - FIXED: Check if function exists
        if (function_exists('addInternalNotification')) {
            addInternalNotification(
                $pdo,
                $guarantorId,
                "Loan Guarantor Request",
                "{$applicantName} has requested you to guarantee a loan of Ksh " . number_format($loanData['loan_amount'], 2) . 
                " for {$loanData['loan_period']} months. Your guarantee amount: Ksh " . number_format($guaranteeAmount, 2) . 
                ". Please review and respond.",
                'info',
                "guarantor-requests.php",
                'carbon:request-quote'
            );
        } else {
            // Fallback: Insert directly into database
            try {
                $stmt = $pdo->prepare("
                    INSERT INTO member_internal_notifications 
                    (member_id, title, message, type, status, created_at)
                    VALUES (?, ?, ?, 'guarantor_request', 'unread', NOW())
                ");
                $stmt->execute([
                    $guarantorId,
                    "Loan Guarantor Request",
                    "{$applicantName} has requested you to guarantee a loan of Ksh " . number_format($loanData['loan_amount'], 2)
                ]);
            } catch (Exception $e) {
                error_log("Failed to add internal notification: " . $e->getMessage());
            }
        }
        
        return ($emailSent || $smsSent);
        
    } catch (Exception $e) {
        error_log("Send guarantor request notification error: " . $e->getMessage());
        return false;
    }
}

// ============================================
// DATABASE WRAPPER CLASS
// ============================================

class DatabaseWrapper {
    private $pdo;
    
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    
    public function fetchAll($sql, $params = []) {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        } catch (PDOException $e) {
            error_log("❌ Database error: " . $e->getMessage());
            throw new Exception('Database error', 500);
        }
    }
    
    public function fetchOne($sql, $params = []) {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
        } catch (PDOException $e) {
            error_log("❌ Database error: " . $e->getMessage());
            throw new Exception('Database error', 500);
        }
    }
    
    public function execute($sql, $params = []) {
        try {
            $stmt = $this->pdo->prepare($sql);
            return $stmt->execute($params);
        } catch (PDOException $e) {
            error_log("❌ Database error: " . $e->getMessage());
            throw new Exception('Database error', 500);
        }
    }
    
    public function insertAndGetId($sql, $params = []) {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $this->pdo->lastInsertId();
        } catch (PDOException $e) {
            error_log("❌ Database error: " . $e->getMessage());
            throw new Exception('Database error', 500);
        }
    }
}

// ============================================
// CUSTOM EXCEPTION CLASS
// ============================================

class LoanApplicationException extends Exception {
    private $errorCode;
    private $httpCode;
    
    public function __construct($message, $httpCode = 400, $errorCode = 'VALIDATION_ERROR') {
        parent::__construct($message);
        $this->httpCode = $httpCode;
        $this->errorCode = $errorCode;
    }
    
    public function getErrorCode() {
        return $this->errorCode;
    }
    
    public function getHttpCode() {
        return $this->httpCode;
    }
    
    public function toJsonResponse() {
        http_response_code($this->httpCode);
        
        return [
            'success' => false,
            'status' => 'error',
            'error_code' => $this->errorCode,
            'message' => $this->getMessage(),
            'timestamp' => date('Y-m-d H:i:s')
        ];
    }
}

// ============================================
// LOAN CHARGES CONFIGURATION
// ============================================

class LoanChargesManager {
    private $db;
    
    public function __construct($pdo) {
        $this->db = new DatabaseWrapper($pdo);
    }
    
    public function getLoanCharges($loanTypeId) {
        try {
            return $this->db->fetchAll("
                SELECT id, charge_type, charge_value, value_type, description
                FROM account_charges
                WHERE account_type_id = ? AND status = 'active'
            ", [$loanTypeId]);
        } catch (Exception $e) {
            error_log("Failed to fetch loan charges: " . $e->getMessage());
            return [];
        }
    }
    
    public function calculateCharges($loanAmount, $charges) {
        $totalCharges = 0;
        $calculatedCharges = [];
        
        foreach ($charges as $charge) {
            $chargeAmount = 0;
            
            if ($charge['value_type'] === 'percentage') {
                $chargeAmount = $loanAmount * ($charge['charge_value'] / 100);
            } else {
                $chargeAmount = $charge['charge_value'];
            }
            
            $totalCharges += $chargeAmount;
            
            $calculatedCharges[] = [
                'charge_id' => $charge['id'],
                'charge_type' => $charge['charge_type'],
                'charge_amount' => round($chargeAmount, 2),
                'value_type' => $charge['value_type'],
                'charge_value' => $charge['charge_value'],
                'description' => $charge['description'] ?? null
            ];
        }
        
        return [
            'total_charges' => round($totalCharges, 2),
            'charges' => $calculatedCharges,
            'total_loan_amount' => $loanAmount + $totalCharges
        ];
    }
    
    public function saveLoanCharges($loanId, $charges) {
        try {
            foreach ($charges as $charge) {
                $this->db->execute("
                    INSERT INTO loan_charges (
                        loan_id, charge_id, charge_type, charge_amount,
                        value_type, created_at
                    ) VALUES (?, ?, ?, ?, ?, NOW())
                ", [
                    $loanId,
                    $charge['charge_id'],
                    $charge['charge_type'],
                    $charge['charge_amount'],
                    $charge['value_type']
                ]);
            }
            return true;
        } catch (Exception $e) {
            error_log("Failed to save loan charges: " . $e->getMessage());
            throw new Exception("Failed to save loan charges");
        }
    }
}

// ============================================
// HELPER FUNCTIONS
// ============================================

function apiLog($message, $level = 'INFO') {
    $logEntry = "[" . date('Y-m-d H:i:s') . "] [$level] " . $message;
    error_log($logEntry);
    
    $logFile = __DIR__ . '/logs/loan-application-api_' . date('Y-m-d') . '.log';
    if (!file_exists(dirname($logFile))) {
        mkdir(dirname($logFile), 0755, true);
    }
    file_put_contents($logFile, $logEntry . PHP_EOL, FILE_APPEND);
}

function maskContactInfo($data, $type) {
    if (empty($data)) return 'Not provided';
    
    switch ($type) {
        case 'phone':
            if (strlen($data) >= 7) {
                $prefix = substr($data, 0, 4);
                $suffix = substr($data, -2);
                return $prefix . '******' . $suffix;
            }
            return substr($data, 0, 4) . '******';
            
        case 'email':
            $parts = explode('@', $data);
            if (count($parts) === 2) {
                $username = $parts[0];
                $domain = $parts[1];
                if (strlen($username) > 3) {
                    $maskedUsername = substr($username, 0, 3) . '***';
                } else {
                    $maskedUsername = substr($username, 0, 1) . '***';
                }
                return $maskedUsername . '@' . $domain;
            }
            return substr($data, 0, 3) . '***@****';
            
        case 'id':
            return maskIdNumber($data);
            
        default:
            return $data;
    }
}

function maskIdNumber($idNumber) {
    if (empty($idNumber) || strlen($idNumber) < 4) {
        return '****';
    }
    $first = substr($idNumber, 0, 2);
    $last = substr($idNumber, -2);
    $length = strlen($idNumber);
    $mask = str_repeat('*', $length - 4);
    return $first . $mask . $last;
}

function getMemberDetails($pdo, $memberId) {
    $db = new DatabaseWrapper($pdo);
    return $db->fetchOne("
        SELECT 
            id, first_name, last_name, member_number, 
            personal_number, phone_number, email_address,
            registration_date, date_of_birth, id_number, status
        FROM members 
        WHERE id = ? AND status = 'active'
    ", [$memberId]);
}

function getMemberSavingsAccounts($pdo, $memberId) {
    $db = new DatabaseWrapper($pdo);
    return $db->fetchAll("
        SELECT 
            ma.id,
            ma.account_number,
            ma.available_balance,
            at.account_name,
            at.account_type,
            at.type,
            ma.sub_account_code
        FROM member_accounts ma
        JOIN account_types at ON ma.account_type_id = at.id
        WHERE ma.member_id = ? 
        AND at.account_type = 'savings'
        AND ma.status = 'active'
        ORDER BY at.account_name
    ", [$memberId]);
}

function getLoanAccountTypes($pdo) {
    $db = new DatabaseWrapper($pdo);
    return $db->fetchAll("
        SELECT 
            at.id, 
            at.account_name, 
            at.type_code,
            at.max_loan_factor,
            at.interest_rate,
            at.min_loan_amount,
            at.max_loan_amount,
            at.description,
            at.self_guarantee_percentage,
            at.basis_savings_account,
            (
                SELECT COUNT(*) 
                FROM account_charges ac 
                WHERE ac.account_type_id = at.id 
                
            ) as charges_count
        FROM account_types at
        WHERE at.account_type = 'loan'
        AND at.status = 1
        ORDER BY at.account_name
    ");
}

function getLoanPurposes($pdo) {
    $db = new DatabaseWrapper($pdo);
    return $db->fetchAll("
        SELECT 
            id, 
            purpose_name, 
            purpose_code,
            description
        FROM loan_purposes
        WHERE status = 1
        AND is_selectable = 1
        ORDER BY purpose_name
    ");
}

function calculateMaxEligibility($availableBalance, $loanFactor) {
    return $availableBalance * $loanFactor;
}

function calculateLoanCharges($pdo, $loanTypeId, $loanAmount) {
    $chargesManager = new LoanChargesManager($pdo);
    $charges = $chargesManager->getLoanCharges($loanTypeId);
    return $chargesManager->calculateCharges($loanAmount, $charges);
}

function validateLoanApplicationWithCharges($data, $maxEligibility, $chargesInfo) {
    $errors = [];
    
    $required = ['account_type_id', 'savings_account_id', 'loan_purpose_id', 'loan_amount', 'loan_period'];
    foreach ($required as $field) {
        if (!isset($data[$field]) || empty($data[$field])) {
            $errors[] = ucfirst(str_replace('_', ' ', $field)) . " is required";
        }
    }
    
    if (!empty($errors)) {
        return ['valid' => false, 'errors' => $errors];
    }
    
    $loanAmount = (float)$data['loan_amount'];
    if ($loanAmount < 1000) {
        $errors[] = "Minimum loan amount is Ksh 1,000";
    }
    
    $totalWithCharges = $loanAmount + $chargesInfo['total_charges'];
    
    if ($totalWithCharges > $maxEligibility) {
        $errors[] = "Total amount (loan + charges) exceeds maximum eligibility of Ksh " . 
                   number_format($maxEligibility, 2) . ". " .
                   "Charges: Ksh " . number_format($chargesInfo['total_charges'], 2) . ", " .
                   "Total: Ksh " . number_format($totalWithCharges, 2);
    }
    
    $allowedPeriods = [6, 12, 24, 36];
    $loanPeriod = (int)$data['loan_period'];
    if (!in_array($loanPeriod, $allowedPeriods)) {
        $errors[] = "Invalid loan period selected. Allowed: " . implode(', ', $allowedPeriods) . " months";
    }
    
    if (!empty($errors)) {
        return ['valid' => false, 'errors' => $errors];
    }
    
    return [
        'valid' => true,
        'data' => [
            'account_type_id' => (int)$data['account_type_id'],
            'savings_account_id' => (int)$data['savings_account_id'],
            'loan_purpose_id' => (int)$data['loan_purpose_id'],
            'loan_amount' => $loanAmount,
            'loan_period' => $loanPeriod,
            'total_charges' => $chargesInfo['total_charges'],
            'total_amount_with_charges' => $totalWithCharges
        ]
    ];
}

function calculateGuaranteeRequirements($loanAmount, $availableBalance, $includeCharges = false, $chargesAmount = 0) {
    if ($includeCharges) {
        $loanAmount += $chargesAmount;
    }
    
    $selfGuaranteeAmount = min($loanAmount, $availableBalance);
    $remainingAmount = $loanAmount - $selfGuaranteeAmount;
    $needGuarantors = $remainingAmount > 0;
    
    return [
        'self_guarantee_amount' => $selfGuaranteeAmount,
        'remaining_amount' => $remainingAmount,
        'need_guarantors' => $needGuarantors,
        'available_balance' => $availableBalance,
        'total_amount_considered' => $loanAmount,
        'charges_included' => $includeCharges
    ];
}

function generateLoanNumber($pdo = null) {
    $loanNumber = 'LN' . time() . rand(100, 999);
    
    if ($pdo) {
        $db = new DatabaseWrapper($pdo);
        $existing = $db->fetchOne("SELECT id FROM loans WHERE loan_number = ?", [$loanNumber]);
        if ($existing) {
            return generateLoanNumber($pdo);
        }
    }
    
    return $loanNumber;
}

function getEligibleGuaranteeAccountTypes() {
    return ['Non-Withdrawable', 'Withdrawable'];
}

/**
 * Check if member has existing pending/active loan of same product type
 */
function checkExistingLoanOfSameProduct($pdo, $memberId, $accountTypeId) {
    $db = new DatabaseWrapper($pdo);
    
    $existingLoan = $db->fetchOne("
        SELECT id, loan_number, loan_amount, status, created_at
        FROM loans 
        WHERE member_id = ? 
        AND account_type_id = ? 
        AND status IN ('pending', 'active', 'approved', 'disbursed')
        LIMIT 1
    ", [$memberId, $accountTypeId]);
    
    if ($existingLoan) {
        return [
            'has_existing' => true,
            'loan' => $existingLoan
        ];
    }
    
    return ['has_existing' => false];
}

function verifyGuarantor($pdo, $memberId, $searchTerm, $guaranteeAmount) {
    apiLog("=== VERIFY GUARANTOR START ===");
    apiLog("Applicant Member ID: " . $memberId);
    apiLog("Search Term: " . $searchTerm);
    apiLog("Guarantee Amount: " . $guaranteeAmount);
    
    $db = new DatabaseWrapper($pdo);
    
    try {
        $searchTerm = trim($searchTerm);
        apiLog("Cleaned search term: '" . $searchTerm . "'");
        
        if (empty($searchTerm)) {
            return ['success' => false, 'message' => "Please enter a search term"];
        }
        
        if ($guaranteeAmount < 100) {
            return ['success' => false, 'message' => "Minimum guarantee amount is Ksh 100"];
        }
        
        $searchStrategies = [
            'personal_number' => "personal_number = ?",
            'member_number' => "member_number = ?",
            'id_number' => "id_number = ?",
            'phone_number' => "phone_number = ?"
        ];
        
        $guarantor = null;
        $foundBy = null;
        
        foreach ($searchStrategies as $field => $condition) {
            $query = "
                SELECT 
                    id, first_name, last_name, email_address, phone_number,
                    personal_number, member_number, id_number, date_of_birth,
                    registration_date, status, communication_preference
                FROM members 
                WHERE $condition 
                AND id != ?
                AND status = 'active'
            ";
            
            $result = $db->fetchOne($query, [$searchTerm, $memberId]);
            
            if ($result) {
                apiLog("✓ Found by $field: " . json_encode($result));
                $guarantor = $result;
                $foundBy = $field;
                break;
            }
        }
        
        if (!$guarantor) {
            $selfQuery = "
                SELECT id, first_name, last_name 
                FROM members 
                WHERE (personal_number = ? OR member_number = ? OR id_number = ? OR phone_number = ?)
                AND id = ?
            ";
            $selfResult = $db->fetchOne($selfQuery, [$searchTerm, $searchTerm, $searchTerm, $searchTerm, $memberId]);
            
            if ($selfResult) {
                apiLog("Member found but it's the applicant themselves");
                return ['success' => false, 'message' => "Cannot use yourself as a guarantor"];
            }
            
            apiLog("❌ No matching member found");
            return ['success' => false, 'message' => "Member not found. Please check the member number, ID number, or phone number."];
        }
        
        apiLog("Guarantor ID: " . $guarantor['id']);
        apiLog("Guarantor Name: " . $guarantor['first_name'] . ' ' . $guarantor['last_name']);
        
        $savingsQuery = "
            SELECT 
                ma.id,
                ma.account_number,
                ma.available_balance,
                at.account_name,
                at.account_type,
                at.type,
                ma.sub_account_code
            FROM member_accounts ma
            JOIN account_types at ON ma.account_type_id = at.id
            WHERE ma.member_id = ? 
            AND at.account_type = 'savings'
            AND ma.status = 'active'
            AND at.type = 'Non-Withdrawable'
            ORDER BY ma.available_balance DESC
        ";
        
        $savingsAccounts = $db->fetchAll($savingsQuery, [$guarantor['id']]);
        
        apiLog("Found " . count($savingsAccounts) . " eligible savings accounts");
        
        if (empty($savingsAccounts)) {
            $eligibleTypes = getEligibleGuaranteeAccountTypes();
            $accountNames = implode(' or ', $eligibleTypes);
            
            return [
                'success' => false,
                'message' => "Member has no eligible savings accounts for guarantees. " .
                           "Only " . $accountNames . " accounts can be used for guarantees.",
                'eligible_account_types' => $eligibleTypes
            ];
        }
        
        $eligibleAccount = null;
        
        foreach ($savingsAccounts as $account) {
            if ($account['available_balance'] >= $guaranteeAmount) {
                $eligibleAccount = $account;
                break;
            }
        }
        
        if (!$eligibleAccount) {
            $highestAccount = $savingsAccounts[0];
            $eligibleTypes = getEligibleGuaranteeAccountTypes();
            $accountNames = implode(' or ', $eligibleTypes);
            
            return [
                'success' => false,
                'message' => "Insufficient savings balance in eligible accounts. " .
                           "Only " . $accountNames . " accounts can be used for guarantees. " .
                           "Available in " . $highestAccount['account_name'] . " (" . $highestAccount['type'] . "): Ksh " . 
                           number_format($highestAccount['available_balance'], 2) . 
                           ", Required: Ksh " . number_format($guaranteeAmount, 2),
                'eligible_account_types' => $eligibleTypes,
                'available_balance' => $highestAccount['available_balance'],
                'account_name' => $highestAccount['account_name'],
                'account_type' => $highestAccount['type']
            ];
        }
        
        apiLog("✓ Eligible account found: " . $eligibleAccount['account_name']);
        
        $age = null;
        if (!empty($guarantor['date_of_birth'])) {
            $dob = new DateTime($guarantor['date_of_birth']);
            $today = new DateTime();
            $age = $today->diff($dob)->y;
        }
        
        apiLog("=== VERIFY GUARANTOR END - SUCCESS ===");
        
        return [
            'success' => true,
            'guarantor' => [
                'id' => $guarantor['id'],
                'search_type' => $foundBy,
                'name' => $guarantor['first_name'] . ' ' . $guarantor['last_name'],
                'email' => maskContactInfo($guarantor['email_address'], 'email'),
                'phone' => maskContactInfo($guarantor['phone_number'], 'phone'),
                'personal_number' => $guarantor['personal_number'],
                'member_number' => $guarantor['member_number'],
                'id_number' => maskContactInfo($guarantor['id_number'], 'id'),
                'age' => $age,
                'registration_date' => $guarantor['registration_date'],
                'available_balance' => $eligibleAccount['available_balance'],
                'account_name' => $eligibleAccount['account_name'],
                'account_type' => $eligibleAccount['type'],
                'account_number' => $eligibleAccount['account_number'],
                'account_id' => $eligibleAccount['id'],
                'sub_account_code' => $eligibleAccount['sub_account_code'],
                'eligible_for_guarantee' => true
            ]
        ];
        
    } catch (Exception $e) {
        apiLog("❌ Database Exception: " . $e->getMessage(), 'ERROR');
        return ['success' => false, 'message' => "Database error occurred while verifying guarantor"];
    }
}

function getLoanDetailsWithCharges($pdo, $loanId, $memberId) {
    $db = new DatabaseWrapper($pdo);
    
    try {
        $loan = $db->fetchOne("
            SELECT 
                l.*,
                at.account_name as loan_type_name,
                lp.purpose_name,
                (
                    SELECT SUM(charge_amount) 
                    FROM loan_charges 
                    WHERE loan_id = l.id
                ) as total_charges
            FROM loans l
            LEFT JOIN account_types at ON l.account_type_id = at.id
            LEFT JOIN loan_purposes lp ON l.loan_purpose_id = lp.id
            WHERE l.id = ? AND l.member_id = ?
        ", [$loanId, $memberId]);
        
        if ($loan) {
            $loan['charges'] = $db->fetchAll("
                SELECT 
                    charge_type,
                    charge_amount,
                    value_type,
                    created_at
                FROM loan_charges 
                WHERE loan_id = ?
                ORDER BY created_at ASC
            ", [$loanId]);
        }
        
        return $loan;
    } catch (Exception $e) {
        apiLog("Failed to fetch loan details: " . $e->getMessage(), 'ERROR');
        return null;
    }
}

function getLoanGuarantors($pdo, $loanId, $memberId) {
    $db = new DatabaseWrapper($pdo);
    
    try {
        $loan = $db->fetchOne("
            SELECT id, loan_number, status 
            FROM loans 
            WHERE id = ? AND member_id = ?
        ", [$loanId, $memberId]);
        
        if (!$loan) {
            return null;
        }
        
        $guarantors = $db->fetchAll("
            SELECT 
                lg.id as guarantee_id,
                lg.guarantor_member_id,
                lg.requested_amount,
                lg.status as guarantee_status,
                lg.created_at as guarantee_date,
                lg.original_balance,
                lg.responded_at,
                m.first_name,
                m.last_name,
                m.member_number,
                m.personal_number,
                m.phone_number,
                m.email_address,
                at.account_name,
                at.type as account_type,
                ma.account_number,
                ma.available_balance as current_balance
            FROM loan_guarantors lg
            JOIN members m ON lg.guarantor_member_id = m.id
            JOIN member_accounts ma ON lg.savings_account_id = ma.id
            JOIN account_types at ON ma.account_type_id = at.id
            WHERE lg.loan_id = ?
            ORDER BY lg.created_at ASC
        ", [$loanId]);
        
        foreach ($guarantors as &$guarantor) {
            $guarantor['phone'] = maskContactInfo($guarantor['phone_number'], 'phone');
            $guarantor['email'] = maskContactInfo($guarantor['email_address'], 'email');
            $guarantor['full_name'] = $guarantor['first_name'] . ' ' . $guarantor['last_name'];
            unset($guarantor['phone_number'], $guarantor['email_address']);
        }
        
        return [
            'loan' => $loan,
            'guarantors' => $guarantors,
            'total_guarantee_amount' => array_sum(array_column($guarantors, 'requested_amount')),
            'guarantor_count' => count($guarantors),
            'pending_count' => count(array_filter($guarantors, function($g) {
                return $g['guarantee_status'] === 'pending';
            })),
            'accepted_count' => count(array_filter($guarantors, function($g) {
                return $g['guarantee_status'] === 'accepted';
            })),
            'declined_count' => count(array_filter($guarantors, function($g) {
                return $g['guarantee_status'] === 'declined';
            }))
        ];
    } catch (Exception $e) {
        apiLog("Failed to fetch loan guarantors: " . $e->getMessage(), 'ERROR');
        return null;
    }
}

function getMemberLoanApplications($pdo, $memberId) {
    $db = new DatabaseWrapper($pdo);
    
    try {
        $loans = $db->fetchAll("
            SELECT 
                l.id,
                l.loan_number,
                l.loan_amount,
                l.loan_period,
                l.status,
                l.application_date,
                l.total_charges,
                l.interest_rate,
                l.self_guaranteed_amount,
                l.total_guaranteed_amount,
                l.guarantor_count,
                at.account_name as loan_type,
                lp.purpose_name,
                (
                    SELECT SUM(charge_amount) 
                    FROM loan_charges lc 
                    WHERE lc.loan_id = l.id
                ) as actual_charges,
                (
                    SELECT COUNT(*) 
                    FROM loan_guarantors lg 
                    WHERE lg.loan_id = l.id
                ) as actual_guarantor_count
            FROM loans l
            LEFT JOIN account_types at ON l.account_type_id = at.id
            LEFT JOIN loan_purposes lp ON l.loan_purpose_id = lp.id
            WHERE l.member_id = ?
            ORDER BY l.application_date DESC
        ", [$memberId]);
        
        foreach ($loans as &$loan) {
            $loan['charges_details'] = $db->fetchAll("
                SELECT charge_type, charge_amount
                FROM loan_charges 
                WHERE loan_id = ?
                ORDER BY created_at ASC
            ", [$loan['id']]);
        }
        
        return $loans;
    } catch (Exception $e) {
        apiLog("Failed to fetch loan applications: " . $e->getMessage(), 'ERROR');
        return [];
    }
}

function validateAffordability($availableBalance, $loanAmount, $chargesInfo) {
    $totalRequired = $loanAmount + $chargesInfo['total_charges'];
    
    if ($totalRequired > $availableBalance) {
        $shortfall = $totalRequired - $availableBalance;
        return [
            'affordable' => false,
            'shortfall' => $shortfall,
            'message' => "Insufficient balance to cover loan and charges. " .
                        "Available: Ksh " . number_format($availableBalance, 2) . ", " .
                        "Required: Ksh " . number_format($totalRequired, 2) . ", " .
                        "Shortfall: Ksh " . number_format($shortfall, 2)
        ];
    }
    
    return [
        'affordable' => true,
        'available_balance' => $availableBalance,
        'total_required' => $totalRequired,
        'remaining_balance' => $availableBalance - $totalRequired
    ];
}

// ============================================
// REQUEST HANDLERS
// ============================================

function handleGetRequest($pdo, $memberId, $action) {
    apiLog("Processing GET request with action: " . ($action ?? 'none'));
    
    switch ($action) {
        case 'loan_data':
            try {
                $member = getMemberDetails($pdo, $memberId);
                $savingsAccounts = getMemberSavingsAccounts($pdo, $memberId);
                $loanAccountTypes = getLoanAccountTypes($pdo);
                $loanPurposes = getLoanPurposes($pdo);
                
                if (!$member) {
                    http_response_code(404);
                    echo json_encode(['success' => false, 'error' => 'Member not found or inactive']);
                    return;
                }
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'member' => $member,
                        'savings_accounts' => $savingsAccounts,
                        'loan_account_types' => $loanAccountTypes,
                        'loan_purposes' => $loanPurposes
                    ]
                ], JSON_NUMERIC_CHECK);
                
                apiLog("Loan data retrieved successfully for member: " . $memberId);
                
            } catch (Exception $e) {
                apiLog("Failed to fetch loan data: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to fetch loan data',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'eligibility':
            $savingsAccountId = $_GET['savings_account_id'] ?? null;
            $accountTypeId = $_GET['account_type_id'] ?? null;
            
            if (!$savingsAccountId || !$accountTypeId) {
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => 'Savings account ID and account type ID are required'
                ]);
                return;
            }
            
            try {
                $db = new DatabaseWrapper($pdo);
                
                $account = $db->fetchOne("
                    SELECT ma.available_balance, at.account_name, at.type
                    FROM member_accounts ma
                    JOIN account_types at ON ma.account_type_id = at.id
                    WHERE ma.id = ? AND ma.member_id = ?
                ", [$savingsAccountId, $memberId]);
                
                if (!$account) {
                    http_response_code(404);
                    echo json_encode(['success' => false, 'error' => 'Savings account not found']);
                    return;
                }
                
                $loanType = $db->fetchOne("
                    SELECT max_loan_factor, account_name 
                    FROM account_types 
                    WHERE id = ?
                ", [$accountTypeId]);
                
                if (!$loanType) {
                    http_response_code(404);
                    echo json_encode(['success' => false, 'error' => 'Loan account type not found']);
                    return;
                }
                
                $availableBalance = (float)$account['available_balance'];
                $maxLoanFactor = (float)$loanType['max_loan_factor'];
                $maxEligibility = calculateMaxEligibility($availableBalance, $maxLoanFactor);
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'savings_account' => $account['account_name'],
                        'account_type' => $account['type'],
                        'loan_type' => $loanType['account_name'],
                        'available_balance' => $availableBalance,
                        'max_loan_factor' => $maxLoanFactor,
                        'max_eligibility' => $maxEligibility,
                        'calculation' => "{$availableBalance} × {$maxLoanFactor}"
                    ]
                ], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                apiLog("Failed to calculate eligibility: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to calculate eligibility',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'guarantee_calculation':
            $loanAmount = $_GET['loan_amount'] ?? null;
            $savingsAccountId = $_GET['savings_account_id'] ?? null;
            
            if (!$loanAmount || !$savingsAccountId) {
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => 'Loan amount and savings account ID are required'
                ]);
                return;
            }
            
            try {
                $db = new DatabaseWrapper($pdo);
                
                $account = $db->fetchOne("
                    SELECT available_balance, account_number, 
                           (SELECT type FROM account_types WHERE id = ma.account_type_id) as account_type
                    FROM member_accounts ma
                    WHERE ma.id = ? AND ma.member_id = ?
                ", [$savingsAccountId, $memberId]);
                
                if (!$account) {
                    http_response_code(404);
                    echo json_encode(['success' => false, 'error' => 'Savings account not found']);
                    return;
                }
                
                $loanAmount = (float)$loanAmount;
                $availableBalance = (float)$account['available_balance'];
                $guarantee = calculateGuaranteeRequirements($loanAmount, $availableBalance);
                
                echo json_encode([
                    'success' => true,
                    'data' => array_merge($guarantee, [
                        'account_number' => $account['account_number'],
                        'account_type' => $account['account_type']
                    ])
                ], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                apiLog("Failed to calculate guarantee: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to calculate guarantee requirements',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'loan_charges':
            $loanTypeId = $_GET['loan_type_id'] ?? null;
            $loanAmount = $_GET['loan_amount'] ?? null;
            
            if (!$loanTypeId || !$loanAmount) {
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => 'Loan type ID and amount are required'
                ]);
                return;
            }
            
            try {
                $chargesInfo = calculateLoanCharges($pdo, $loanTypeId, (float)$loanAmount);
                
                echo json_encode([
                    'success' => true,
                    'data' => $chargesInfo
                ], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                apiLog("Failed to calculate charges: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to calculate loan charges',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'loan_details':
            $loanId = $_GET['loan_id'] ?? null;
            
            if (!$loanId) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'Loan ID is required']);
                return;
            }
            
            try {
                $loan = getLoanDetailsWithCharges($pdo, $loanId, $memberId);
                
                if (!$loan) {
                    http_response_code(404);
                    echo json_encode(['success' => false, 'error' => 'Loan not found']);
                    return;
                }
                
                echo json_encode(['success' => true, 'data' => $loan], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                apiLog("Failed to fetch loan details: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to fetch loan details',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'loan_guarantors':
            $loanId = $_GET['loan_id'] ?? null;
            
            if (!$loanId) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'Loan ID is required']);
                return;
            }
            
            try {
                $result = getLoanGuarantors($pdo, $loanId, $memberId);
                
                if (!$result) {
                    http_response_code(404);
                    echo json_encode(['success' => false, 'error' => 'Loan not found']);
                    return;
                }
                
                echo json_encode([
                    'success' => true,
                    'data' => $result
                ], JSON_NUMERIC_CHECK);
                
                apiLog("Retrieved " . $result['guarantor_count'] . " guarantors for loan: " . $loanId);
                
            } catch (Exception $e) {
                apiLog("Failed to fetch loan guarantors: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to fetch guarantors',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'my_loans':
            try {
                $loans = getMemberLoanApplications($pdo, $memberId);
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'loans' => $loans,
                        'count' => count($loans),
                        'total_active' => count(array_filter($loans, function($loan) {
                            return in_array($loan['status'], ['active', 'pending', 'approved']);
                        }))
                    ]
                ], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                apiLog("Failed to fetch member loans: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to fetch loan applications',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'verify_guarantor':
            $memberNumber = $_GET['member_number'] ?? null;
            $guaranteeAmount = $_GET['guarantee_amount'] ?? null;
            
            if (!$memberNumber || !$guaranteeAmount) {
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => 'Member number and guarantee amount are required'
                ]);
                return;
            }
            
            try {
                $result = verifyGuarantor($pdo, $memberId, $memberNumber, (float)$guaranteeAmount);
                
                if ($result['success']) {
                    echo json_encode([
                        'success' => true,
                        'data' => $result['guarantor']
                    ], JSON_NUMERIC_CHECK);
                } else {
                    http_response_code(400);
                    echo json_encode([
                        'success' => false,
                        'error' => $result['message'],
                        'eligible_account_types' => getEligibleGuaranteeAccountTypes()
                    ]);
                }
                
            } catch (Exception $e) {
                apiLog("Failed to verify guarantor: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to verify guarantor',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'eligible_account_types':
            try {
                $eligibleTypes = getEligibleGuaranteeAccountTypes();
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'eligible_types' => $eligibleTypes,
                        'count' => count($eligibleTypes),
                        'description' => 'Only these account types can be used for loan guarantees'
                    ]
                ], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to fetch eligible account types',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        case 'current_application':
            $loanApp = $_SESSION['loan_application'] ?? null;
            $tempGuarantors = $_SESSION['temp_guarantors'] ?? [];
            
            echo json_encode([
                'success' => true,
                'data' => [
                    'loan_application' => $loanApp,
                    'temp_guarantors' => $tempGuarantors,
                    'has_active_application' => !empty($loanApp)
                ]
            ], JSON_NUMERIC_CHECK);
            break;
            
        default:
            try {
                $savingsAccounts = getMemberSavingsAccounts($pdo, $memberId);
                $loanAccountTypes = getLoanAccountTypes($pdo);
                $eligibleGuaranteeTypes = getEligibleGuaranteeAccountTypes();
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'savings_accounts' => $savingsAccounts,
                        'loan_account_types' => $loanAccountTypes,
                        'eligible_guarantee_account_types' => $eligibleGuaranteeTypes,
                        'active_application' => isset($_SESSION['loan_application']),
                        'charges_note' => 'Charges are fetched dynamically from the database based on loan type'
                    ]
                ], JSON_NUMERIC_CHECK);
                
            } catch (Exception $e) {
                apiLog("Failed to fetch loan information: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to fetch loan information',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
    }
}

function handlePostRequest($pdo, $memberId, $action) {
    $input = json_decode(file_get_contents('php://input'), true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'error' => 'Invalid JSON input',
            'json_error' => json_last_error_msg()
        ]);
        return;
    }
    
    apiLog("Processing POST request with action: " . ($action ?? 'none'));
    
    switch ($action) {
        case 'submit_full_application':
            // Submit complete loan application with guarantors in one request
            if (!isset($input['savings_account_id']) || !isset($input['account_type_id']) || 
                !isset($input['loan_amount']) || !isset($input['loan_period']) || !isset($input['loan_purpose_id'])) {
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => 'Missing required fields',
                    'required_fields' => ['savings_account_id', 'account_type_id', 'loan_amount', 'loan_period', 'loan_purpose_id']
                ]);
                return;
            }
            
            try {
                $pdo->beginTransaction();
                $db = new DatabaseWrapper($pdo);
                $chargesManager = new LoanChargesManager($pdo);
                
                apiLog("Starting full loan application submission with charges and guarantors");
                
                // CHECK: Existing loan of same product
                $existingLoanCheck = checkExistingLoanOfSameProduct($pdo, $memberId, $input['account_type_id']);
                if ($existingLoanCheck['has_existing']) {
                    $existing = $existingLoanCheck['loan'];
                    throw new Exception("You already have a " . ucfirst($existing['status']) . " loan of this product type. Loan #{$existing['loan_number']} (Ksh " . 
                                       number_format($existing['loan_amount'], 2) . " applied on " . date('Y-m-d', strtotime($existing['created_at'])) . 
                                       "). Please complete or cancel that loan before applying for another.");
                }
                
                // Get savings account details
                $savingsAccount = $db->fetchOne("
                    SELECT ma.available_balance, at.account_name, ma.account_number, at.type
                    FROM member_accounts ma
                    JOIN account_types at ON ma.account_type_id = at.id
                    WHERE ma.id = ? AND ma.member_id = ?
                ", [$input['savings_account_id'], $memberId]);
                
                if (!$savingsAccount) {
                    throw new Exception("Invalid savings account selected");
                }
                
                // Get loan account type details
                $loanAccountType = $db->fetchOne("
                    SELECT max_loan_factor, account_name, interest_rate, id
                    FROM account_types 
                    WHERE id = ?
                ", [$input['account_type_id']]);
                
                if (!$loanAccountType) {
                    throw new Exception("Invalid loan account type selected");
                }
                
                // Get loan purpose
                $loanPurpose = $db->fetchOne("
                    SELECT purpose_name, purpose_code
                    FROM loan_purposes 
                    WHERE id = ?
                ", [$input['loan_purpose_id']]);
                
                // Calculate loan charges
                $chargesInfo = calculateLoanCharges($pdo, $input['account_type_id'], (float)$input['loan_amount']);
                
                // Calculate maximum eligibility
                $maxEligibility = calculateMaxEligibility(
                    $savingsAccount['available_balance'], 
                    $loanAccountType['max_loan_factor']
                );
                
                // Validate application data
                $validation = validateLoanApplicationWithCharges($input, $maxEligibility, $chargesInfo);
                
                if (!$validation['valid']) {
                    http_response_code(400);
                    echo json_encode([
                        'success' => false,
                        'error' => 'Validation failed',
                        'errors' => $validation['errors']
                    ]);
                    $pdo->rollBack();
                    return;
                }
                
                $loanData = $validation['data'];
                
                // Calculate guarantee requirements
                $guarantee = calculateGuaranteeRequirements(
                    $loanData['loan_amount'],
                    $savingsAccount['available_balance']
                );
                
                // Generate loan number
                $loanNumber = generateLoanNumber($pdo);
                
                // Get interest rate
                $interestRate = $loanAccountType['interest_rate'] ?? 12.0;
                
                // Calculate monthly installment for notification
                $totalInterest = ($loanData['loan_amount'] * $interestRate * $loanData['loan_period']) / (12 * 100);
                $totalPayable = $loanData['loan_amount'] + $totalInterest + $loanData['total_charges'];
                $monthlyInstallment = $totalPayable / $loanData['loan_period'];
                
                // Create loan record
                $loanId = $db->insertAndGetId("
                    INSERT INTO loans (
                        member_id, loan_number, account_type_id, 
                        savings_account_id, loan_purpose_id, loan_amount, 
                        loan_period, status, application_date,
                        self_guaranteed_amount, total_guaranteed_amount,
                        interest_rate, total_charges, created_at,
                        description
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', NOW(), ?, 0, ?, ?, NOW(), ?)
                ", [
                    $memberId,
                    $loanNumber,
                    $loanData['account_type_id'],
                    $loanData['savings_account_id'],
                    $loanData['loan_purpose_id'],
                    $loanData['loan_amount'],
                    $loanData['loan_period'],
                    $guarantee['self_guarantee_amount'],
                    $interestRate,
                    $loanData['total_charges'],
                    $input['description'] ?? null
                ]);
                
                // Save loan charges
                $chargesManager->saveLoanCharges($loanId, $chargesInfo['charges']);
                
                // Process guarantors if provided
                $addedGuarantors = [];
                $guarantorDetails = [];
                $totalGuaranteed = 0;
                
                // Get applicant name for notifications
                $applicant = getMemberDetails($pdo, $memberId);
                $applicantName = $applicant['first_name'] . ' ' . $applicant['last_name'];
                
                $loanNotificationData = [
                    'loan_number' => $loanNumber,
                    'loan_amount' => $loanData['loan_amount'],
                    'loan_period' => $loanData['loan_period'],
                    'interest_rate' => $interestRate,
                    'monthly_installment' => $monthlyInstallment
                ];
                
                if (isset($input['guarantors']) && is_array($input['guarantors']) && count($input['guarantors']) > 0) {
                    apiLog("Processing " . count($input['guarantors']) . " guarantors");
                    
                    foreach ($input['guarantors'] as $guarantorData) {
                        // Verify each guarantor
                        $verification = verifyGuarantor(
                            $pdo, 
                            $memberId, 
                            $guarantorData['search_term'], 
                            (float)$guarantorData['amount']
                        );
                        
                        if (!$verification['success']) {
                            throw new Exception("Guarantor verification failed: " . $verification['message']);
                        }
                        
                        $verifiedGuarantor = $verification['guarantor'];
                        $guaranteeAmount = (float)$guarantorData['amount'];
                        
                        // Check if total exceeds remaining amount
                        if ($totalGuaranteed + $guaranteeAmount > $guarantee['remaining_amount']) {
                            throw new Exception("Total guarantee amount exceeds required amount of " . 
                                               number_format($guarantee['remaining_amount'], 2));
                        }
                        
                        // Record guarantor relationship
                        $db->execute("
                            INSERT INTO loan_guarantors 
                            (loan_id, guarantor_member_id, requested_amount, savings_account_id, 
                             added_by, status, original_balance, created_at)
                            VALUES (?, ?, ?, ?, ?, 'pending', ?, NOW())
                        ", [
                            $loanId,
                            $verifiedGuarantor['id'],
                            $guaranteeAmount,
                            $verifiedGuarantor['account_id'],
                            $memberId,
                            $verifiedGuarantor['available_balance']
                        ]);
                        
                        $guarantorRecordId = $pdo->lastInsertId();
                        $totalGuaranteed += $guaranteeAmount;
                        
                        // Send notification to guarantor (ONLY after successful DB insert)
                        sendGuarantorRequestNotification(
                            $pdo,
                            $verifiedGuarantor['id'],
                            $loanNotificationData,
                            $applicantName,
                            $guaranteeAmount
                        );
                        
                        // Store details for response
                        $guarantorDetails[] = [
                            'id' => $guarantorRecordId,
                            'guarantor_id' => $verifiedGuarantor['id'],
                            'name' => $verifiedGuarantor['name'],
                            'member_number' => $verifiedGuarantor['member_number'],
                            'personal_number' => $verifiedGuarantor['personal_number'],
                            'phone' => $verifiedGuarantor['phone'],
                            'email' => $verifiedGuarantor['email'],
                            'amount' => $guaranteeAmount,
                            'account_name' => $verifiedGuarantor['account_name'],
                            'account_number' => $verifiedGuarantor['account_number'],
                            'account_type' => $verifiedGuarantor['account_type'],
                            'available_balance' => $verifiedGuarantor['available_balance'],
                            'status' => 'pending',
                            'added_at' => date('Y-m-d H:i:s')
                        ];
                        
                        $addedGuarantors[] = [
                            'id' => $guarantorRecordId,
                            'guarantor_id' => $verifiedGuarantor['id'],
                            'name' => $verifiedGuarantor['name'],
                            'amount' => $guaranteeAmount,
                            'account_type' => $verifiedGuarantor['account_type']
                        ];
                    }
                    
                    // Update loan with guarantor information
                    $totalCoverage = $guarantee['self_guarantee_amount'] + $totalGuaranteed;
                    
                    if ($totalCoverage < $loanData['loan_amount']) {
                        $shortfall = $loanData['loan_amount'] - $totalCoverage;
                        throw new Exception("Insufficient coverage. Additional Ksh " . number_format($shortfall, 2) . " needed");
                    }
                    
                    $db->execute("
                        UPDATE loans 
                        SET total_guaranteed_amount = ?,
                            guarantor_count = ?,
                            updated_at = NOW(),
                            status = 'pending'
                        WHERE id = ?
                    ", [
                        $totalGuaranteed,
                        count($input['guarantors']),
                        $loanId
                    ]);
                }
                
                $pdo->commit();
                
                // Send notification to loanee (applicant) ONLY after successful commit
                $loanNotificationData['total_charges'] = $loanData['total_charges'];
                $loanNotificationData['self_guarantee_amount'] = $guarantee['self_guarantee_amount'];
                $loanNotificationData['guarantor_count'] = count($addedGuarantors);
                
                sendLoaneeApplicationNotification($pdo, $memberId, $loanNotificationData);
                
                // Clear any session data
                unset($_SESSION['loan_application'], $_SESSION['temp_guarantors']);
                
                apiLog("Full loan application submitted successfully: " . $loanNumber);
                
                echo json_encode([
                    'success' => true,
                    'message' => 'Loan application submitted successfully' . (count($addedGuarantors) > 0 ? ' with guarantors' : ''),
                    'data' => [
                        'loan_id' => $loanId,
                        'loan_number' => $loanNumber,
                        'loan_amount' => $loanData['loan_amount'],
                        'loan_period' => $loanData['loan_period'],
                        'interest_rate' => $interestRate,
                        'monthly_installment' => round($monthlyInstallment, 2),
                        'total_charges' => $loanData['total_charges'],
                        'total_amount_with_charges' => $loanData['total_amount_with_charges'],
                        'charges_breakdown' => $chargesInfo['charges'],
                        'total_coverage' => ($guarantee['self_guarantee_amount'] + $totalGuaranteed),
                        'self_guarantee' => $guarantee['self_guarantee_amount'],
                        'external_guarantee' => $totalGuaranteed,
                        'guarantor_count' => count($addedGuarantors),
                        'status' => count($addedGuarantors) > 0 ? 'pending' : 'pending',
                        'application_date' => date('Y-m-d H:i:s'),
                        'savings_account' => [
                            'id' => $loanData['savings_account_id'],
                            'number' => $savingsAccount['account_number'],
                            'name' => $savingsAccount['account_name'],
                            'type' => $savingsAccount['type'],
                            'available_balance' => $savingsAccount['available_balance']
                        ],
                        'loan_type' => [
                            'id' => $loanData['account_type_id'],
                            'name' => $loanAccountType['account_name'],
                            'interest_rate' => $interestRate,
                            'max_loan_factor' => $loanAccountType['max_loan_factor']
                        ],
                        'loan_purpose' => [
                            'id' => $loanData['loan_purpose_id'],
                            'name' => $loanPurpose['purpose_name'] ?? 'Not specified',
                            'code' => $loanPurpose['purpose_code'] ?? null
                        ],
                        'guarantors' => $guarantorDetails,
                        'added_guarantors' => $addedGuarantors,
                        'next_steps' => count($addedGuarantors) > 0 ? 'await_approval' : 'add_guarantors',
                        'remaining_amount_needed' => count($addedGuarantors) > 0 ? 0 : $guarantee['remaining_amount']
                    ]
                ]);
                
            } catch (Exception $e) {
                $pdo->rollBack();
                apiLog("Full loan application error: " . $e->getMessage(), 'ERROR');
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => $e->getMessage()
                ]);
            }
            break;
            
        // ... rest of the handlers remain the same ...
        
        default:
            http_response_code(400);
            echo json_encode([
                'success' => false,
                'error' => 'Invalid action specified'
            ]);
            break;
    }
}

// ... rest of the file remains the same ...

function handleDeleteRequest($pdo, $memberId, $action) {
    apiLog("Processing DELETE request with action: " . ($action ?? 'none'));
    
    switch ($action) {
        case 'remove_guarantor':
            $index = $_GET['index'] ?? null;
            
            if ($index === null) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'Guarantor index is required']);
                return;
            }
            
            $index = (int)$index;
            
            if (isset($_SESSION['temp_guarantors'][$index])) {
                $removed = $_SESSION['temp_guarantors'][$index];
                array_splice($_SESSION['temp_guarantors'], $index, 1);
                
                echo json_encode([
                    'success' => true,
                    'message' => 'Guarantor removed successfully',
                    'data' => [
                        'removed_guarantor' => $removed,
                        'remaining_guarantors' => $_SESSION['temp_guarantors'] ?? [],
                        'remaining_count' => count($_SESSION['temp_guarantors'] ?? [])
                    ]
                ]);
            } else {
                http_response_code(404);
                echo json_encode(['success' => false, 'error' => 'Guarantor not found']);
            }
            break;
            
        case 'cancel_application':
            $loanId = $_GET['loan_id'] ?? null;
            
            if (!$loanId) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'Loan ID is required']);
                return;
            }
            
            try {
                $db = new DatabaseWrapper($pdo);
                
                $db->execute("
                    UPDATE loans 
                    SET status = 'cancelled', updated_at = NOW()
                    WHERE id = ? AND member_id = ? AND status IN ('pending', 'pending')
                ", [$loanId, $memberId]);
                
                $affectedRows = $pdo->rowCount();
                
                if ($affectedRows === 0) {
                    http_response_code(404);
                    echo json_encode([
                        'success' => false,
                        'error' => 'Loan application not found or cannot be cancelled'
                    ]);
                    return;
                }
                
                if (isset($_SESSION['loan_application']) && $_SESSION['loan_application']['loan_id'] == $loanId) {
                    unset($_SESSION['loan_application'], $_SESSION['temp_guarantors']);
                }
                
                echo json_encode([
                    'success' => true,
                    'message' => 'Loan application cancelled successfully'
                ]);
                
            } catch (Exception $e) {
                apiLog("Cancel loan error: " . $e->getMessage(), 'ERROR');
                http_response_code(500);
                echo json_encode([
                    'success' => false,
                    'error' => 'Failed to cancel loan application',
                    'message' => ENVIRONMENT === 'development' ? $e->getMessage() : null
                ]);
            }
            break;
            
        default:
            http_response_code(400);
            echo json_encode([
                'success' => false,
                'error' => 'Invalid action specified'
            ]);
            break;
    }
}

// ============================================
// MAIN EXECUTION
// ============================================

try {
    apiLog("Request received: " . $method . " " . $_SERVER['REQUEST_URI'] . " Action: " . ($action ?? 'none'));
    
    // Ensure PDO connection is available
    global $pdo;
    if (!isset($pdo) || !$pdo) {
        require_once __DIR__ . '/includes/config.php';
    }
    
    switch ($method) {
        case 'GET':
            handleGetRequest($pdo, $memberId, $action);
            break;
            
        case 'POST':
            handlePostRequest($pdo, $memberId, $action);
            break;
            
        case 'DELETE':
            handleDeleteRequest($pdo, $memberId, $action);
            break;
            
        default:
            http_response_code(405);
            echo json_encode([
                'success' => false,
                'error' => 'Method not allowed',
                'allowed_methods' => ['GET', 'POST', 'DELETE']
            ]);
            break;
    }
} catch (Exception $e) {
    apiLog("Top-level exception: " . $e->getMessage(), 'ERROR');
    http_response_code(500);
    echo json_encode([
        'success' => false,
        'error' => 'Internal server error',
        'message' => 'Something went wrong'
    ]);
}
?>