<?php

$allowedOrigins = [
    'http://localhost:8100',
    'https://localhost',
    'capacitor://localhost', 
    'https://swapeki.com',
    'https://app.swapeki.com'
];

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

if (in_array($origin, $allowedOrigins, true)) {
    header("Access-Control-Allow-Origin: $origin");
    header("Access-Control-Allow-Credentials: true");
}

header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
header("Access-Control-Max-Age: 3600");
header("Content-Type: application/json; charset=UTF-8");

/**
 * 🔑 VERY IMPORTANT
 * Handle preflight OPTIONS request
 */
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

require './conn.php';

$uploadDir = __DIR__ . "/uploads/";
ini_set('display_errors', 0);
ini_set('log_errors', 1);
error_reporting(E_ALL);
// ---------- Step 3: Handle preflight requests ----------
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}
if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
    http_response_code(403);
    exit('Insecure connection');
}


//GET NUMBERS
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
    switch ($_POST['action']) {

case 'delete_promo':

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid session'
        ]));
    }

    $u = getUserInfo($token);
    if (!$u || (int)$u['is_admin'] !== 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Unauthorized'
        ]));
    }

    $id = (int)($_POST['id'] ?? 0);
    if ($id <= 0) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid promo ID'
        ]));
    }

    // Optional: fetch promo to delete image file
    $q = $mysqli->prepare("SELECT image FROM promos WHERE id = ? LIMIT 1");
    $q->bind_param("i", $id);
    $q->execute();
    $res = $q->get_result();
    $promo = $res->fetch_assoc();
    $q->close();

    if (!$promo) {
        exit(json_encode([
            'success' => false,
            'message' => 'Promo not found'
        ]));
    }

    // Delete DB record
    $stmt = $mysqli->prepare("DELETE FROM promos WHERE id = ?");
    $stmt->bind_param("i", $id);

    if (!$stmt->execute()) {
        error_log("PROMO DELETE ERROR: " . $stmt->error);
        exit(json_encode([
            'success' => false,
            'message' => 'Failed to delete promo'
        ]));
    }
    $stmt->close();

    // Optional: delete uploaded image
    if (!empty($promo['image'])) {
        $path = $_SERVER['DOCUMENT_ROOT'] . $promo['image'];
        if (file_exists($path)) {
            @unlink($path);
        }
    }

    echo json_encode([
        'success' => true,
        'message' => 'Promo deleted successfully'
    ]);
    break;


        case 'get_active_promo':

    $now = time();

    $stmt = $mysqli->prepare("
        SELECT id, title, type, image, html, cta_link, show_once
        FROM promos
        WHERE is_active = 1
          AND (start_date IS NULL OR start_date <= ?)
          AND (end_date IS NULL OR end_date >= ?)
        ORDER BY id DESC
        LIMIT 1
    ");

    $stmt->bind_param("ii", $now, $now);
    $stmt->execute();
    $res = $stmt->get_result();
    $promo = $res->fetch_assoc();
    $stmt->close();

    if (!$promo) {
        echo json_encode([
            'success' => true,
            'promo' => null
        ]);
        exit;
    }

    echo json_encode([
        'success' => true,
        'promo' => $promo
    ]);
    break;

 case 'save_promo':

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid session'
        ]));
    }

    $u = getUserInfo($token);
    if (!$u || (int)$u['is_admin'] !== 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Unauthorized'
        ]));
    }

    /* ================= INPUT ================= */

    $id        = isset($_POST['id']) ? (int)$_POST['id'] : null;
    $title     = sanitizeInput($_POST['title'] ?? '');
    $type      = sanitizeInput($_POST['type'] ?? '');
    $html      = $_POST['html'] ?? null;
    $cta_link  = sanitizeInput($_POST['cta_link'] ?? '');
    $show_once = (int)($_POST['show_once'] ?? 1);

    $start_date = !empty($_POST['start_date']) ? (int)$_POST['start_date'] : null;
    $end_date   = !empty($_POST['end_date']) ? (int)$_POST['end_date'] : null;

    if (!$title || !$type || !in_array($type, ['image', 'html'])) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid promo data'
        ]));
    }

    /* ================= IMAGE UPLOAD ================= */

    $imagePath = null;
    $uploadDir = __DIR__ . "/uploads/promos/";

    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }

    if ($type === 'image' && isset($_FILES['image'])) {

        $file = $_FILES['image'];

        if ($file['error'] !== UPLOAD_ERR_OK) {
            exit(json_encode([
                'success' => false,
                'message' => 'Image upload failed'
            ]));
        }

        $allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
        if (!in_array($file['type'], $allowedTypes)) {
            exit(json_encode([
                'success' => false,
                'message' => 'Only JPG, PNG or WEBP images allowed'
            ]));
        }

        if ($file['size'] > (2 * 1024 * 1024)) {
            exit(json_encode([
                'success' => false,
                'message' => 'Image must not exceed 2MB'
            ]));
        }

        $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
        $filename = 'promo_' . time() . '_' . mt_rand(1000, 9999) . '.' . $ext;

        if (!move_uploaded_file($file['tmp_name'], $uploadDir . $filename)) {
            exit(json_encode([
                'success' => false,
                'message' => 'Failed to save image'
            ]));
        }

        $imagePath = '/uploads/promos/' . $filename;
    }

    if ($type === 'image' && !$imagePath && !$id) {
        exit(json_encode([
            'success' => false,
            'message' => 'Promo image is required'
        ]));
    }

    if ($type === 'html' && empty($html)) {
        exit(json_encode([
            'success' => false,
            'message' => 'HTML content is required'
        ]));
    }

    /* ================= DB ================= */

    if ($id) {
        // UPDATE
        $stmt = $mysqli->prepare("
            UPDATE promos SET
                title = ?,
                type = ?,
                image = COALESCE(?, image),
                html = ?,
                cta_link = ?,
                show_once = ?,
                start_date = ?,
                end_date = ?
            WHERE id = ?
        ");

        $stmt->bind_param(
            "sssssiisi",
            $title,
            $type,
            $imagePath,
            $html,
            $cta_link,
            $show_once,
            $start_date,
            $end_date,
            $id
        );

    } else {
        // INSERT
        $created = time();

        $stmt = $mysqli->prepare("
            INSERT INTO promos
                (title, type, image, html, cta_link, show_once, start_date, end_date, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");

        $stmt->bind_param(
            "sssssiisi",
            $title,
            $type,
            $imagePath,
            $html,
            $cta_link,
            $show_once,
            $start_date,
            $end_date,
            $created
        );
    }

    if (!$stmt->execute()) {
        error_log("PROMO SAVE ERROR: " . $stmt->error);
        echo json_encode([
            'success' => false,
            'message' => 'Failed to save promo'
        ]);
        exit;
    }

    $stmt->close();

    echo json_encode([
        'success' => true,
        'message' => 'Promo saved successfully'
    ]);
    break;


    case 'toggle_promo':

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode(['success' => false, 'message' => 'Invalid session']));
    }

    $u = getUserInfo($token);
    if (!$u || $u['is_admin'] != 1) {
        exit(json_encode(['success' => false, 'message' => 'Unauthorized']));
    }

    $id = (int)($_POST['id'] ?? 0);
    $status = (int)($_POST['status'] ?? 0);

    $stmt = $mysqli->prepare("UPDATE promos SET is_active = ? WHERE id = ?");
    $stmt->bind_param("ii", $status, $id);
    $stmt->execute();
    $stmt->close();

    echo json_encode([
        'success' => true,
        'message' => 'Promo status updated'
    ]);
    break;
   
    case 'get_promos':

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode(['success' => false, 'message' => 'Invalid session']));
    }

    $u = getUserInfo($token);
    if (!$u || $u['is_admin'] != 1) {
        exit(json_encode(['success' => false, 'message' => 'Unauthorized']));
    }

    $res = $mysqli->query("
        SELECT * FROM promos
        ORDER BY id DESC
    ");

    $rows = [];
    while ($r = $res->fetch_assoc()) {
        $rows[] = $r;
    }

    echo json_encode([
        'success' => true,
        'data' => $rows
    ]);
    break;

          case 'toggle_balance_visibility':

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    $user = getUserInfo($token);
    if (!$user) {
        exit(json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]));
    }

    // Expect 0 or 1
    $showBal = isset($_POST['show_bal']) ? (int) $_POST['show_bal'] : null;

    if (!in_array($showBal, [0, 1], true)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid value.'
        ]));
    }

    $stmt = $mysqli->prepare("
        UPDATE users 
        SET show_bal = ? 
        WHERE id = ?
    ");
    $stmt->bind_param("ii", $showBal, $user['id']);

    if (!$stmt->execute()) {
        $stmt->close();
        exit(json_encode([
            'success' => false,
            'message' => 'Failed to update preference.'
        ]));
    }

    $stmt->close();

    echo json_encode([
        'success'  => true,
        'show_bal' => $showBal
    ]);
    break;

       case "register_notif_token":
  $user_id = intval($_POST['user_id']);
  $token = $_POST['token'];
//error_log("NOTIF ".$user_id.' - '.$token);
  if(!$user_id || !$token) {
    echo json_encode(['success' => false, 'message' => 'Missing parameters']);
    exit;
  }

   $u = getUserInfo($user_id);

    if (empty($u)) {
        echo json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]);
        exit;
    }

  $stmt = $mysqli->prepare("UPDATE users SET fcm_token = ? WHERE id = ?");
  $stmt->bind_param("si", $token, $user_id);

  if($stmt->execute()){
    echo json_encode(['success' => true, 'message' => 'Token saved']);
  } else {
    echo json_encode(['success' => false, 'message' => 'Failed to save token']);
  }
  break;

    
           case 'get_vtp_balance':
    try {
        $q = $client->request('GET', $vtpBase . 'balance', [
           'headers' => [
                            'Content-Type' => 'application/json',
                           'public-key'   => $vtpPubKey,
                            'api-key'      => $vtpApiKey,
                        ],
        ]);

        $res = json_decode($q->getBody(), true);

        if (isset($res['code']) && $res['code'] == 1) {
            echo json_encode([
                'success' => true,
                'data'    => $res['contents']['balance']
            ]);
        } else {
            echo json_encode([
                'success' => false,
                'message' => 'Failed to fetch vtp balance data.'
            ]);
        }
    } catch (Exception $e) {
        $error = parseError($e);
        echo json_encode([
            'success' => false,
            'message' => $error
        ]);
    }
    break;
        
          case 'get_vtp_services':
    try {
        $q = $client->request('GET', $vtpBase . 'service-categories', [
           'headers' => [
                            'Content-Type' => 'application/json',
                            'public-key'   => $vtpPubKey,
                            'api-key'      => $vtpApiKey,
                        ],
        ]);

        $res = json_decode($q->getBody(), true);

        if (isset($res['response_description']) && $res['response_description'] === '000') {
            echo json_encode([
                'success' => true,
                'data'    => $res['content']
            ]);
        } else {
            echo json_encode([
                'success' => false,
                'message' => 'Failed to fetch services data.'
            ]);
        }
    } catch (Exception $e) {
        $error = parseError($e);
        echo json_encode([
            'success' => false,
            'message' => $error
        ]);
    }
    break;
        
        case 'get_admin_settings':
             $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

     $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
            $res = $mysqli->query("
              SELECT *
              FROM settings 
            ");
            $settings = $res->fetch_assoc();
            echo json_encode(['success' => true, 'data' => $settings]);
            break;
          

             case 'update_settings':
    $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
     if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
    $fields = [
        'admin_email','smtp_host','smtp_email','smtp_password',
        'app_name',
        'quidax_buy_rate','quidax_sell_rate',
        'domain',
        'vtp_base','vtp_api','vtp_pub','api_url','contact_email','contact_phone',
        'contact_address','facebook','x','instagram','youtube','whatsapp',
       'enable_topup','enable_bills','enable_giftcard',
        'enable_crypto','ref_bonus', 'ref_spend',
        'add_electricity', 'add_tv', 'add_education', 'add_data'

    ];

    $data = [];
    foreach ($fields as $field) {
        $data[$field] = sanitizeInput($_POST[$field] ?? '');
    }

    $result = $mysqli->query("SELECT id FROM settings LIMIT 1");
    $isUpdate = ($result && $result->num_rows > 0);

    if ($isUpdate) {
        $setClause = implode(", ", array_map(fn($k) => "$k = ?", array_keys($data)));
        $stmt = $mysqli->prepare("UPDATE settings SET $setClause WHERE id = 1");
    } else {
        $columns = implode(", ", array_keys($data));
        $placeholders = implode(", ", array_fill(0, count($data), "?"));
        $stmt = $mysqli->prepare("INSERT INTO settings ($columns) VALUES ($placeholders)");
    }

    if (!$stmt) {
        echo json_encode(['success' => false, 'message' => 'Database error: '.$mysqli->error]);
        exit;
    }

    $types = str_repeat("s", count($data));
    $stmt->bind_param($types, ...array_values($data));

    if ($stmt->execute()) {
        error_log("Settings updated by ".$admin['email']);
        echo json_encode(['success' => true, 'message' => 'Settings updated successfully.']);
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to update settings.']);
    }

    $stmt->close();
    break;

   case "contact_us":
    $name = $_POST['name'];
    $html = $_POST['html'];

    if (sendMail($set['contact_email'], "New Contact Message from {$name}", $html)) {
        echo json_encode([
            'success' => true,
            'message' => 'Your message has been sent. We will get back to you shortly.'
        ]);
    } else {
        echo json_encode([
            'success' => false,
            'message' => 'Your message could not be sent.'
        ]);
    }
    break;
          

        case 'create_ad':
 $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }


            try {
              $link = $_POST['link'] ?? '#';
          
              // Handle file upload
              if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
                throw new Exception("Image upload failed.");
              }
          
              $uploadDir = 'uploads/ads/';
              if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
          
              $fileName = time() . '_' . basename($_FILES['image']['name']);
              $targetPath = $uploadDir . $fileName;
          
              if (!move_uploaded_file($_FILES['image']['tmp_name'], $targetPath)) {
                throw new Exception("Failed to move uploaded file.");
              }
          
              $stmt = $mysqli->prepare("INSERT INTO ads (image, link) VALUES (?, ?)");
              $stmt->bind_param("ss", $targetPath, $link);
              $stmt->execute();
          
              echo json_encode(['success' => true, 'message' => 'Ad created successfully.']);
            } catch (Throwable $e) {
              echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
            }
            break;
          

        case 'get_pending_giftcard_sales':
            try {
              $res = $mysqli->query("SELECT * FROM gift_card_sales WHERE status = 'Pending' ORDER BY id DESC");
              $sales = [];
          
              while ($row = $res->fetch_assoc()) {
                $user = getUserInfo($row['user_id']);
          
                $row['withdrawal_bank'] = $user['withdrawal_bank'] ?? '';
                $row['withdrawal_bank_name'] = $user['withdrawal_bank_name'] ?? '';
                $row['withdrawal_bank_code'] = $user['withdrawal_bank_code'] ?? '';
                $row['withdrawal_bank_account'] = $user['withdrawal_bank_account'] ?? '';
                $row['user_full_name'] = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? ''));
                $sales[] = $row;
              }
          
              echo json_encode(['success' => true, 'data' => $sales]);
            } catch (Throwable $e) {
              echo json_encode(['success' => false, 'message' => 'Error fetching sales: ' . $e->getMessage()]);
            }
            break;
          
          case 'handle_giftcard_sale_action':
            $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
            try {
              $id = (int)$_POST['id'];
              $action = $_POST['actionType']; 
          
              $res = $mysqli->query("SELECT * FROM gift_card_sales WHERE id = $id LIMIT 1");
              if (!$res || $res->num_rows == 0) {
                echo json_encode(['success' => false, 'message' => 'Sale record not found.']);
                exit;
              }
          
              $sale = $res->fetch_assoc();
              $user = getUserInfo($sale['user_id']);
          
              if (!$user) {
                echo json_encode(['success' => false, 'message' => 'User not found.']);
                exit;
              }
          
              $newStatus = ($action === 'pay') ? 'Complete' : 'Rejected';
              $mysqli->query("UPDATE gift_card_sales SET status = '$newStatus' WHERE id = $id");
          
              // Email content
              $to = $user['email'];
              $name = $user['first_name'];
              $subject = ($action === 'pay') ? "Gift Card Sale Paid" : "Gift Card Sale Rejected";
              $message = ($action === 'pay')
                ? "Dear $name,\n\nYour gift card sale of ₦{$sale['total']} has been successfully processed and paid."
                : "Dear $name,\n\nYour gift card sale of ₦{$sale['total']} has been rejected. Please contact support for details.";
          
              $message .= "<p>Best regards.</p><p>Swapeki Team.</p>";
          $text = emailTemplate($subject, $message);
              sendMail($to, $subject, $text);
          
              echo json_encode(['success' => true, 'message' => "Sale marked as $newStatus and user notified."]);
            } catch (Throwable $e) {
                error_log("GiftCardSaleError: " . $e->getMessage());
                echo json_encode([
                  'success' => false,
                  'message' => 'Error processing sale: ' . $e->getMessage(),
                  'trace' => $e->getTraceAsString() // optional for dev
                ]);
              }
              
            break;
          
        case 'get_all_gift_cards':
            $res = $mysqli->query("SELECT * FROM gift_cards ORDER BY id DESC");
            $cards = [];
            while ($row = $res->fetch_assoc()) $cards[] = $row;
            echo json_encode(['success' => true, 'cards' => $cards]);
            break;

            case 'save_gift_card':
                  $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
                $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
                $categoryId = (int)$_POST['category'];
                $name = $_POST['name'];
                $type = $_POST['type'];
                $rate = (int)$_POST['rate'];
              
                // Get the category name by ID from your categories table
                $catRes = $mysqli->query("SELECT name FROM gift_card_categories WHERE id = $categoryId LIMIT 1");
                $catRow = $catRes->fetch_assoc();
                $categoryName = $catRow['name'] ?? '';
              
                if ($id > 0) {
                  $stmt = $mysqli->prepare("UPDATE gift_cards SET category=?, name=?, type=?, rate=? WHERE id=?");
                  $stmt->bind_param("sssii", $categoryName, $name, $type, $rate, $id);
                } else {
                  $stmt = $mysqli->prepare("INSERT INTO gift_cards (category, name, type, rate) VALUES (?, ?, ?, ?)");
                  $stmt->bind_param("sssi", $categoryName, $name, $type, $rate);
                }
              
                if ($stmt->execute()) {
                  echo json_encode(['success' => true, 'message' => 'Gift card saved.']);
                } else {
                  echo json_encode(['success' => false, 'message' => 'Failed to save.']);
                }
                break;
              

                case 'delete_gift_card':
                      $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
                    $id = (int)$_POST['id'];
                    $res = $mysqli->query("DELETE FROM gift_cards WHERE id = $id");
                  
                    if ($res) {
                      echo json_encode(['success' => true, 'message' => 'Gift card deleted.']);
                    } else {
                      echo json_encode(['success' => false, 'message' => 'Failed to delete gift card.']);
                    }
                    break;
                  

        case 'get_user_stats':
              $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
            $stats = [];
          
            // Total users
            $res = $mysqli->query("SELECT COUNT(*) AS count FROM users");
            $stats['total_users'] = $res->fetch_assoc()['count'];
          
            // Total verified users (email_verified = 1 AND id_verified = 1)
            $res = $mysqli->query("SELECT COUNT(*) AS count FROM users WHERE email_verified = 1 AND id_verified = 1");
            $stats['total_verified'] = $res->fetch_assoc()['count'];
          
            // Total unverified users (either not email_verified or not id_verified)
            $res = $mysqli->query("SELECT COUNT(*) AS count FROM users WHERE email_verified = 0 OR id_verified = 0");
            $stats['total_unverified'] = $res->fetch_assoc()['count'];
          
            // Total users NGN balance
            $res = $mysqli->query("SELECT SUM(CAST(ngn_balance AS DECIMAL(20,2))) AS total_balance FROM users");
            $stats['total_users_balance'] = $res->fetch_assoc()['total_balance'] ?? "0.00";
          
            // Total spent
            $res = $mysqli->query("SELECT SUM(total_spent) AS total_spent FROM users");
            $stats['total_spent'] = $res->fetch_assoc()['total_spent'] ?? "0";

    $res = $mysqli->query("SELECT SUM(CAST(amount AS DECIMAL(20,2))) AS total_ngn_tx FROM transactions WHERE currency = 'NGN' AND flow = 'out' AND status = 'Complete'");
            $stats['total_ngn_tx'] = $res->fetch_assoc()['total_ngn_tx'] ?? "0.00";           
          
            echo json_encode([
              'success' => true,
              'stats' => $stats
            ]);
            break;
          

        case 'get_all_users':
            $result = $mysqli->query("SELECT id, email, phone, username, first_name, last_name, ngn_balance, email_verified, id_verified, is_admin, is_banned FROM users ORDER BY id DESC");

            $users = [];
            while ($row = $result->fetch_assoc()) $users[] = $row;
            echo json_encode(['success' => true, 'users' => $users]);
            break;
        
          case 'search_user_by_email':
              $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $admin = getUserInfo($token);
    if (!$admin || $admin['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
            $email = sanitizeInput($_POST['email']);
            $res = $mysqli->query("SELECT * FROM users WHERE email = '$email' LIMIT 1");
            if ($res && $res->num_rows) {
              echo json_encode(['success' => true, 'user' => $res->fetch_assoc()]);
            } else {
              echo json_encode(['success' => false, 'message' => 'User not found.']);
            }
            break;
        
       

            case 'update_user_details':

    $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    /* 🔐 ADMIN FROM TOKEN */
    $a = getUserInfo($token);
    if (!$a || (int)$a['is_admin'] !== 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Unauthorized request.'
        ]));
    }

    $isSuperAdmin = ((int)$a['super_admin'] === 1);

    /* 🎯 TARGET USER */
    $email = sanitizeInput($_POST['email'], 'email');
    $u = getUserInfo($email);

    if (!$u) {
        exit(json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]));
    }

    $userId    = $u['id'];
    $old_email = $u['email'];

    /* ✅ NORMAL (NON-SENSITIVE) FIELDS */
    $phone      = sanitizeInput($_POST['phone']);
    $first_name = sanitizeInput($_POST['first_name']);
    $last_name  = sanitizeInput($_POST['last_name']);
    $country    = sanitizeInput($_POST['country']);
    $state      = sanitizeInput($_POST['state']);
    $city       = sanitizeInput($_POST['city']);
    $address    = sanitizeInput($_POST['address']);
    $is_banned  = intval($_POST['is_banned']);
    $kyc  = intval($_POST['kyc']);
  $email_verified = intval($_POST['email_verified']);
   $id_verified = intval($_POST['id_verified']);
    /* 🔒 SENSITIVE FIELDS — DEFAULT TO CURRENT VALUES */
    $ngn_balance    = (float)$u['ngn_balance'];
    $is_admin       = (int)$u['is_admin'];

    /* 🔐 SUPER ADMIN OVERRIDES ONLY */
    if ($isSuperAdmin) {
        if (isset($_POST['ngn_balance'])) {
            $ngn_balance = (float) sanitizeInput($_POST['ngn_balance']);
        }


        if (isset($_POST['is_admin'])) {
            $is_admin = intval($_POST['is_admin']);
        }
    }

    /* 🚀 TRANSACTION */
    $mysqli->begin_transaction();

    try {
        $time = time();

        /* 🧮 BALANCE DIFF (SUPER ADMIN ONLY) */
        $ngn_diff = round($ngn_balance - (float)$u['ngn_balance'], 2);

        /* 🔄 UPDATE USER */
        $stmt = $mysqli->prepare("
            UPDATE users 
            SET email=?, phone=?, first_name=?, last_name=?,
                ngn_balance=?,
                country=?, state=?, city=?, address=?,
                email_verified=?, id_verified=?, is_admin=?, is_banned=?, kyc=?
            WHERE id=?
        ");

        $stmt->bind_param(
            "ssssdssssiiiiii",
            $email, $phone, $first_name, $last_name,
            $ngn_balance,
            $country, $state, $city, $address,
            $email_verified, $id_verified, $is_admin, $is_banned, $kyc,
            $userId
        );
        $stmt->execute();
        $stmt->close();

        /* 📧 UPDATE TRANSACTIONS EMAIL */
        if ($old_email !== $email) {
            $stmt2 = $mysqli->prepare(
                "UPDATE transactions SET email=? WHERE email=?"
            );
            $stmt2->bind_param("ss", $email, $old_email);
            $stmt2->execute();
            $stmt2->close();

        }

        /* 🧾 LOG BALANCE ADJUSTMENTS (SUPER ADMIN ONLY) */
        if ($isSuperAdmin) {
            $logTransaction = function($currency, $amount, $flow) use (
                $mysqli, $email, $userId, $a, $time
            ) {
                $title  = "Super Admin Balance Adjustment";
                $desc   = "Manual {$currency} balance {$flow} by Super Admin";
                $status = "Complete";
                $ref    = "ADM" . strtoupper(bin2hex(random_bytes(4)));

                $stmt3 = $mysqli->prepare("
                    INSERT INTO transactions
                    (flow, currency, title, description, date, amount, status, our_ref, email, user_id, type)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'adjustment')
                ");
                $stmt3->bind_param(
                    "sssssdssss",
                    $flow, $currency, $title, $desc,
                    $time, $amount, $status, $ref,
                    $email, $userId
                );
                $stmt3->execute();
                $stmt3->close();

                error_log(
                    "SUPER_ADMIN {$a['email']} adjusted {$currency} {$flow} {$amount} for {$email}"
                );
            };

            if ($ngn_diff !== 0) {
                $logTransaction('NGN', abs($ngn_diff), $ngn_diff > 0 ? 'in' : 'out');
            }

           
        }

        $mysqli->commit();

        echo json_encode([
            'success' => true,
            'message' => 'User details updated successfully.'
        ]);

    } catch (Exception $e) {
        $mysqli->rollback();
        error_log("Admin update failed: " . $e->getMessage());

        echo json_encode([
            'success' => false,
            'message' => 'Failed to update user details.'
        ]);
    }

break;
        
          case 'delete_user':
             $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    /* 🔐 ADMIN FROM TOKEN */
    $a = getUserInfo($token);
    if (!$a || (int)$a['super_admin'] !== 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Unauthorized request.'
        ]));
    }
            $id = sanitizeInput($_POST['user_id']);
            $mysqli->query("DELETE FROM users WHERE id = $id");
            echo json_encode(['success' => true, 'message' => 'User deleted.']);
            break;
        
case "vtp_pay_tv":
    $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
    $serviceID         = sanitizeInput($_POST['serviceID']);
    $amount            = (float)sanitizeInput($_POST['amount']);
    $request_id        = sanitizeInput($_POST['request_id']);
    $billersCode       = sanitizeInput($_POST['billersCode']);
    $variation_code    = sanitizeInput($_POST['variation_code']);
    $phone             = sanitizeInput($_POST['phone']);
    $subscription_type = sanitizeInput($_POST['subscription_type']);

     $userFromToken = getUserInfo($token);
$email = $userFromToken['email'];
$tId = $userFromToken['id'];

 $type    = 'tv';
   if (!acquireActionLock($tId, $type)) {
    exit(json_encode([
        'success' => false,
        'message' => 'Too many requests. cool down.'
    ]));
}
    try {
        // --- Begin transaction ---
        $mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);

        if ($amount < 100) {
            throw new Exception("Invalid amount.");
        }

        // --- Prevent duplicate request IDs ---
        $dup = $mysqli->prepare("SELECT id FROM transactions WHERE our_ref = ? LIMIT 1");
        $dup->bind_param("s", $request_id);
        $dup->execute();
        if ($dup->get_result()->num_rows > 0) {
            throw new Exception("Duplicate request ID.");
        }
        $dup->close();

        // --- Lock user row ---
        $stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ? FOR UPDATE");
        $stmt->bind_param("s", $email);
        $stmt->execute();
        $result = $stmt->get_result();
        $user = $result->fetch_assoc();
        $stmt->close();

        if (!$user) {
            throw new Exception("User not found.");
        }

        if ((int)$user['id_verified'] !== 1) {
            throw new Exception("ID not verified");
            } 

        if ((int)$user['is_banned'] === 1) {
            throw new Exception("Your account is restricted from making transactions.");
        }

        if ($user['ngn_balance'] < $amount) {
            throw new Exception("Insufficient funds.");
        }

        // --- Verify balance integrity ---
        $check = verifyUserBalance($user['id']);
        if (!$check['valid']) {
            throw new Exception("Balance verification error.");
        }

        // --- Debit wallet securely ---
        $debitQuery = $mysqli->prepare("
            UPDATE users 
            SET ngn_balance = ngn_balance - ?, total_spent = total_spent + ? 
            WHERE id = ? AND ngn_balance >= ?
        ");
        $debitQuery->bind_param("ddid", $amount, $amount, $user['id'], $amount);
        if (!$debitQuery->execute() || $debitQuery->affected_rows === 0) {
            throw new Exception("Failed to debit wallet.");
        }
        $debitQuery->close();

        // --- Record initial transaction ---
        $status  = 'processing';
        $title   = 'TV Subscription';
        $desc    = 'Cable TV payment';
        $time    = time();
        $ref     = 'N/A';
        $our_ref = $request_id;
       
        $userId  = $user['id'];

        $insertTxn = $mysqli->prepare("
            INSERT INTO transactions 
            (flow, currency, title, description, date, amount, status, our_ref, email, user_id, type)
            VALUES ('out', 'NGN', ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");
        $insertTxn->bind_param(
            "sssdsssis",
            $title,
            $desc,
            $time,
            $amount,
            $status,
            $our_ref,
            $email,
            $userId,
            $type
        );
        $insertTxn->execute();
        $insertTxn->close();

        // --- Commit debit before API call ---
        $mysqli->commit();

        // --- Call VTpass API ---
        $response = $client->request('POST', $vtpBase . 'pay', [
            'headers' => [
                'Content-Type' => 'application/json',
                'secret-key'   => $vtpSecKey,
                'api-key'      => $vtpApiKey,
            ],
            'body' => json_encode([
                "serviceID"         => $serviceID,
                "amount"            => $amount,
                "request_id"        => $request_id,
                "billersCode"       => $billersCode,
                "variation_code"    => $variation_code,
                "phone"             => $phone,
                "subscription_type" => $subscription_type,
            ]),
        ]);

        // --- Timeout / empty response fallback ---
        if (!$response || !$response->getBody()) {
            $pend = $mysqli->prepare("INSERT INTO pending_tx (tx_id, email, amount, type)
                                      VALUES (?, ?, ?, 'tv')");
            $pend->bind_param("ssd", $request_id, $email, $amount);
            $pend->execute();
            $pend->close();
             releaseActionLock($tId, $type);
            echo json_encode(['success' => false, 'message' => 'Provider timeout — marked pending.']);
            exit;
        }

        $res = json_decode($response->getBody(), true);

        // --- Handle successful or chargeable response ---
        if (isset($res['code']) && !in_array($res['code'], $nonChargeableFailureCodes)) {
            $ref = $res['content']['transactions']['transactionId'] ?? 'N/A';
            $ref = sanitizeInput($ref);
            $payloadJson = json_encode($res);

            $transactionQuery = $mysqli->prepare("
                UPDATE transactions 
                SET status = 'pending', payload = ?, ref = ? 
                WHERE our_ref = ?
            ");
            $transactionQuery->bind_param("sss", $payloadJson, $ref, $request_id);
            if (!$transactionQuery->execute()) {
                throw new Exception("Transaction record failed: " . $transactionQuery->error);
            }
            $transactionQuery->close();

            // Add to pending_tx
            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'tv')
            ");
            $pendQuery->bind_param("ssd", $request_id, $email, $amount);
            $pendQuery->execute();
            $pendQuery->close();
 releaseActionLock($tId, $type);
 payRef($tId);
 applyCashback(
    $tId,
    $our_ref,
    $amount
 );

            echo json_encode([
                'success' => true,
                'message' => $res['content']['transactions']['status'] ?? 'TV subscription successful.',
                'data'    => $res
            ]);
        } else {
            // --- Failure / nonchargeable case ---
            $transactionQuery = $mysqli->prepare("UPDATE transactions SET status= 'Failed' WHERE our_ref=?");
            $transactionQuery->bind_param("s", $request_id);
            $transactionQuery->execute();
            $transactionQuery->close();

            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'tv')
            ");
            $pendQuery->bind_param("ssd", $request_id, $email, $amount);
            $pendQuery->execute();
            $pendQuery->close();
 releaseActionLock($tId, $type);
            echo json_encode([
                'success' => false,
                'message' => $res['response_description'] ?? 'TV subscription failed.',
            ]);
        }

    } catch (Exception $e) {
        $mysqli->rollback();
         releaseActionLock($tId, $type);
        $error = parseError($e);

        echo json_encode([
            'success' => false,
            'message' => 'Transaction error: ' . $error
        ]);
    }

    break;


          case "verify_profile_id":
            $billersCode = sanitizeInput($_POST['billersCode']);
            $serviceID = sanitizeInput($_POST['serviceID']);
            $type = sanitizeInput($_POST['type']);
        
            try {
        
                $response = $client->request('POST', $vtpBase . 'merchant-verify', [
                    'headers' => [
                        'Content-Type' => 'application/json',
                      'secret-key' => $vtpSecKey,
                        'api-key' => $vtpApiKey,
                    ],
                    'body' => json_encode([
                        "serviceID" => $serviceID,
                        "billersCode" => $billersCode,
                        "type" => $type,
                    ]),
                ]);
        
                $res = json_decode($response->getBody(), true);
        
                if (isset($res['code']) && $res['code'] === "000" && empty($res['content']['error'])) {
                    echo json_encode([
                        'success' => true,
                        'message' => 'Profile ID verified.',
                        'data' => $res
                    ]);
                } else {
                   
                    echo json_encode([
                        'success' => false,
                        'message' => $res['content']['error'] ?? $res['response_description'] ?? 'Verification failed.',
                        'data' => $res
                    ]);
                }
            } catch (Exception $e) {
        $error = parseError($e);
                echo json_encode([
                    'success' => false,
                    'message' => 'VTpass profile ID verification failed: ' . $error
                ]);
            }
        
            break;
        

     

    case "top_up_data":
        $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
    $phone         = sanitizeInput($_POST['phone']);
    $amount        = (float)sanitizeInput($_POST['amount']);
    $serviceID     = sanitizeInput($_POST['serviceID']);
    $reqID         = sanitizeInput($_POST['request_id']);
    $billerCode    = sanitizeInput($_POST['billersCode']);
    $variationCode = sanitizeInput($_POST['variation_code']);
 $userFromToken = getUserInfo($token);
$email = $userFromToken['email'];
$tId = $userFromToken['id'];
$type    = 'data';
  if (!acquireActionLock($tId, $type)) {
    exit(json_encode([
        'success' => false,
        'message' => 'Too many requests. cool down.'
    ]));
}
    try {
        // --- Begin secure transaction ---
        $mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);

        if ($amount < 50) {
            throw new Exception("Invalid amount.");
        }

        // --- Prevent duplicate request IDs ---
        $dup = $mysqli->prepare("SELECT id FROM transactions WHERE our_ref = ? LIMIT 1");
        $dup->bind_param("s", $reqID);
        $dup->execute();
        if ($dup->get_result()->num_rows > 0) {
            throw new Exception("Duplicate request ID.");
        }
        $dup->close();

        // --- Lock user row for balance safety ---
        $stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ? FOR UPDATE");
        $stmt->bind_param("s", $email);
        $stmt->execute();
        $result = $stmt->get_result();
        $user = $result->fetch_assoc();
        $stmt->close();

        if (!$user) {
            throw new Exception("User not found.");
        }

        if ((int)$user['is_banned'] === 1) {
            throw new Exception("Your account is restricted from making transactions.");
        }

        if ($user['ngn_balance'] < $amount) {
            throw new Exception("Insufficient funds.");
        }
         if ($user['id_verified'] !== 1) {
            throw new Exception("ID not verified");
            } 

        // --- Balance integrity check ---
        $check = verifyUserBalance($user['id']);
        if (!$check['valid']) {
            throw new Exception("Something failed.");
        }

        // --- Debit wallet securely ---
        $debitQuery = $mysqli->prepare("
            UPDATE users 
            SET ngn_balance = ngn_balance - ?, total_spent = total_spent + ? 
            WHERE id = ? AND ngn_balance >= ?
        ");
        $debitQuery->bind_param("ddid", $amount, $amount, $user['id'], $amount);
        if (!$debitQuery->execute() || $debitQuery->affected_rows === 0) {
            throw new Exception("Failed to debit wallet.");
        }
        $debitQuery->close();

        // --- Insert initial transaction record ---
        $status  = 'processing';
        $title   = 'Data purchase';
        $desc    = 'Data purchase';
        $time    = time();
        $ref     = 'N/A';
        $our_ref = $reqID;
        
        $userId  = $user['id'];

        $insertTxn = $mysqli->prepare("
            INSERT INTO transactions 
            (flow, currency, title, description, date, amount, status, our_ref, email, user_id, type)
            VALUES ('out', 'NGN', ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");
        $insertTxn->bind_param(
            "sssdsssis",
            $title,
            $desc,
            $time,
            $amount,
            $status,
            $reqID,
            $email,
            $userId,
            $type
        );
        $insertTxn->execute();
        $insertTxn->close();

        // --- Commit the debit immediately ---
        $mysqli->commit();

        // --- Call VTpass API ---
        $response = $client->request('POST', $vtpBase . 'pay', [
            'headers' => [
                'Content-Type' => 'application/json',
                'secret-key'   => $vtpSecKey,
                'api-key'      => $vtpApiKey,
            ],
            'body' => json_encode([
                "request_id"     => $reqID,
                "serviceID"      => $serviceID,
                "amount"         => $amount,
                "phone"          => $phone,
                "billersCode"    => $billerCode,
                "variation_code" => $variationCode,
            ]),
        ]);

        // --- Handle timeout or no response ---
        if (!$response || !$response->getBody()) {
            $pend = $mysqli->prepare("INSERT INTO pending_tx (tx_id, email, amount, type)
                                      VALUES (?, ?, ?, 'data')");
            $pend->bind_param("ssd", $reqID, $email, $amount);
            $pend->execute();
            $pend->close();
            releaseActionLock($tId, $type);
            echo json_encode(['success' => false, 'message' => 'Provider timeout — marked pending.']);
            exit;
        }

        $res = json_decode($response->getBody(), true);

        // --- Handle valid VTpass response ---
        if (isset($res['code']) && !in_array($res['code'], $nonChargeableFailureCodes)) {
            $ref = $res['content']['transactions']['transactionId'] ?? 'N/A';
            $ref = sanitizeInput($ref);
            $payloadJson = json_encode($res);

            $transactionQuery = $mysqli->prepare("
                UPDATE transactions 
                SET status = 'pending', payload = ?, ref = ? 
                WHERE our_ref = ?
            ");
            $transactionQuery->bind_param("sss", $payloadJson, $ref, $reqID);
            if (!$transactionQuery->execute()) {
                throw new Exception("Transaction record failed: " . $transactionQuery->error);
            }
            $transactionQuery->close();

            // Also log to pending_tx for tracking
            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'data')
            ");
            $pendQuery->bind_param("ssd", $reqID, $email, $amount);
            $pendQuery->execute();
            $pendQuery->close();
releaseActionLock($tId, $type);
payRef($tId);
 applyCashback(
    $tId,
    $reqID,
    $amount
 );
            echo json_encode([
                'success' => true,
                'message' => $res['content']['transactions']['status'] ?? "Top-up successful",
            ]);
        } else {
            // --- Handle failed or nonchargeable error ---
            $transactionQuery = $mysqli->prepare("UPDATE transactions SET status= 'Failed' WHERE our_ref=?");
            $transactionQuery->bind_param("s", $reqID);
            $transactionQuery->execute();
            $transactionQuery->close();

            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'data')
            ");
            $pendQuery->bind_param("ssd", $reqID, $email, $amount);
            $pendQuery->execute();
            $pendQuery->close();
releaseActionLock($tId, $type);
            echo json_encode([
                'success' => false,
                'message' => $res['response_description'] ?? "Top-up failed",
            ]);
        }

    } catch (Exception $e) {
        $mysqli->rollback();
        releaseActionLock($tId, $type);
        $error = parseError($e);

        echo json_encode([
            'success' => false,
            'message' => 'Transaction error: ' . $error
        ]);
    }

    break;
            

        case "top_up_airtime":
            $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
                $phone     = sanitizeInput($_POST['phone']);
                $amount    = (float)sanitizeInput($_POST['amount']);
                $serviceID = sanitizeInput($_POST['serviceID']);
                $reqID     = sanitizeInput($_POST['request_id']);
             $userFromToken = getUserInfo($token);
$email = $userFromToken['email'];
$tId = $userFromToken['id'];
 $type    = 'airtime'; 
  if (!acquireActionLock($tId, $type)) {
    exit(json_encode([
        'success' => false,
        'message' => 'Too many requests. cool down.'
    ]));
}
                try {
               
                    $mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
  if ($amount < 100) {
                        throw new Exception("Invalid amount.");
                    }
                    $dup = $mysqli->prepare("SELECT id FROM transactions WHERE our_ref = ? LIMIT 1");
$dup->bind_param("s", $reqID);
$dup->execute();
if ($dup->get_result()->num_rows > 0) {
    throw new Exception("Duplicate request ID.");
}
$dup->close();

            
                    $stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ? FOR UPDATE");
                    $stmt->bind_param("s", $email);
                    $stmt->execute();
                    $result = $stmt->get_result();
                    $user = $result->fetch_assoc();
                    $stmt->close();
            
                    if (!$user) {
                        throw new Exception("User not found.");
                    }

        if ((int)$user['id_verified'] !== 1) {
            throw new Exception("ID not verified");
            }   
            
            
            
                    if ((int)$user['is_banned'] === 1) {
                        throw new Exception("Your account is restricted from making transactions.");
                    }
            
                    if ($user['ngn_balance'] < $amount) {
                        throw new Exception("Insufficient funds.");
                    }
                      $check = verifyUserBalance($user['id']);
if (!$check['valid']) {
    throw new Exception("Something failed.");
  
}
             $debitQuery = $mysqli->prepare("
            UPDATE users 
            SET ngn_balance = ngn_balance - ?, total_spent = total_spent + ? 
            WHERE id = ? AND ngn_balance >= ?
        ");
        $debitQuery->bind_param("ddid", $amount, $amount, $user['id'], $amount);
        if (!$debitQuery->execute() || $debitQuery->affected_rows === 0) {
            throw new Exception("Failed to debit wallet.");
        }

        $status = 'processing';
         $title   = 'Airtime purchase';
    $desc    = 'Airtime purchase';
    $time    = time();
    $ref     = 'N/A';
    $our_ref = $reqID;
 $userId = $user['id'];
$insertTxn = $mysqli->prepare("
    INSERT INTO transactions 
    (flow, currency, title, description, date, amount, status, our_ref, email, user_id, type) 
    VALUES ('out', 'NGN', ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$insertTxn->bind_param("sssdsssis", $title, $desc, $time, $amount, $status, $reqID, $email, $userId, $type);
$insertTxn->execute();

                 
                    $mysqli->commit();
            
                    $response = $client->request('POST', $vtpBase . 'pay', [
                        'headers' => [
                            'Content-Type' => 'application/json',
                            'secret-key'   => $vtpSecKey,
                            'api-key'      => $vtpApiKey,
                        ],
                        'body' => json_encode([
                            "request_id" => $reqID,
                            "serviceID"  => $serviceID,
                            "amount"     => $amount,
                            "phone"      => $phone,
                        ]),
                    ]);

                    if (!$response || !$response->getBody()) {
    $pend = $mysqli->prepare("INSERT INTO pending_tx (tx_id, email, amount, type)
                              VALUES (?, ?, ?, 'airtime')");
    $pend->bind_param("ssd", $reqID, $email, $amount);
    $pend->execute();
     releaseActionLock($tId, $type);
    echo json_encode(['success' => false, 'message' => 'Provider timeout — marked pending.']);
    exit;
}

            
                    $res = json_decode($response->getBody(), true);
            
                    if (isset($res['code']) && !in_array($res['code'], $nonChargeableFailureCodes)) {
      
    $ref     = sanitizeInput($res['content']['transactions']['transactionId']) ?? null;
                      
 $userId = $user['id'];
$payloadJson = json_encode($res);

$transactionQuery = $mysqli->prepare("UPDATE transactions SET status= 'pending', payload = ?, ref = ? WHERE our_ref=?");

$transactionQuery->bind_param("sss", $payloadJson, $ref, $reqID);

        if (!$transactionQuery->execute()) {
            throw new Exception("Transaction record failed: " . $transactionQuery->error);
        }    
        $transactionQuery->close();
              $pendQuery = $mysqli->prepare("
            INSERT INTO pending_tx 
            (tx_id, email, amount, type) 
            VALUES (?, ?, ?, 'airtime')
        ");
        $pendQuery->bind_param("ssd", $reqID, $email, $amount);
        if (!$pendQuery->execute()) {
            throw new Exception("Transaction failed: ");
        }
         releaseActionLock($tId, $type);
         payRef($tId);
          applyCashback(
    $tId,
    $reqID,
    $amount
 );

                        echo json_encode([
                            'success' => true,
                            'message' => $res['content']['transactions']['status'] ?? "Top-up successful",
                            'data'    => $res
                        ]);
                    } else {
$transactionQuery = $mysqli->prepare("UPDATE transactions SET status= 'Failed' WHERE our_ref=?");

$transactionQuery->bind_param("s", $reqID);
$transactionQuery->execute();
$transactionQuery->close();
 $pendQuery = $mysqli->prepare("
            INSERT INTO pending_tx 
            (tx_id, email, amount, type) 
            VALUES (?, ?, ?, 'airtime')
        ");
        $pendQuery->bind_param("ssd", $reqID, $email, $amount);
        if (!$pendQuery->execute()) {
            throw new Exception("Transaction failed: ");
        }
         releaseActionLock($tId, $type);
                        echo json_encode([
                            'success' => false,
                            'message' => $res['response_description'] ?? "Top-up failed",
                        ]);
                    }
            
                } catch (Exception $e) {
                   $mysqli->rollback();
                    releaseActionLock($tId, $type);
            $error = parseError($e);
                    echo json_encode([
                        'success' => false,
                        'message' => 'Transaction error: ' . $error
                    ]);
                }
            
                break;
            
    
            

            case "verify_smile_email":
            $email = sanitizeInput($_POST['email'], 'email');
            $serviceID = sanitizeInput($_POST['serviceID']);
        
            try {
        
                $response = $client->request('POST', $vtpBase .'merchant-verify/smile/email', [
                    'headers' => [
                        'Content-Type' => 'application/json',
                       'secret-key' => $vtpSecKey,
                        'api-key' => $vtpApiKey,
                    ],
                    'body' => json_encode([
                        "billersCode" => $email,
                        "serviceID" => $serviceID,
                    ]),
                ]);
        
                $res = json_decode($response->getBody(), true);
        
                if (
                    isset($res['code']) && $res['code'] === "000" &&
                    isset($res['content']['AccountList']['Account'][0]['AccountId'])
                ) {
                    echo json_encode([
                        'success' => true,
                        'message' => 'Smile ID verified.',
                        'data' => $res
                    ]);
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => $res['response_description'] ?? 'Smile email not found.',
                        'data' => $res
                    ]);
                }
        
            } catch (Exception $e) {
                        $error = parseError($e);

                echo json_encode([
                    'success' => false,
                    'message' => 'VTpass Smile email verification failed: ' . $error
                ]);
            }
        
            break;
        

            
   case "vtp_pay_electricity":
            $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
    $serviceID      = sanitizeInput($_POST['serviceID']);
    $amount         = (float)sanitizeInput($_POST['amount']);
    $request_id     = sanitizeInput($_POST['request_id']);
    $billersCode    = sanitizeInput($_POST['billersCode']);
    $variation_code = sanitizeInput($_POST['variation_code']);
    $phone          = sanitizeInput($_POST['phone']);
 
    $userFromToken = getUserInfo($token);
$email = $userFromToken['email'];
$tId = $userFromToken['id'];

 $type    = 'electricity';
  if (!acquireActionLock($tId, $type)) {
    exit(json_encode([
        'success' => false,
        'message' => 'Too many requests. cool down.'
    ]));
}

    try {
        // --- Begin transaction ---
        $mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);

        if ($amount < 100) {
            throw new Exception("Invalid amount.");
        }

        // --- Prevent duplicate request IDs ---
        $dup = $mysqli->prepare("SELECT id FROM transactions WHERE our_ref = ? LIMIT 1");
        $dup->bind_param("s", $request_id);
        $dup->execute();
        if ($dup->get_result()->num_rows > 0) {
            throw new Exception("Duplicate request ID.");
        }
        $dup->close();

        // --- Lock user for concurrency safety ---
        $stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ? FOR UPDATE");
        $stmt->bind_param("s", $email);
        $stmt->execute();
        $result = $stmt->get_result();
        $user = $result->fetch_assoc();
        $stmt->close();

        if (!$user) {
            throw new Exception("User not found.");
        }

         if ($user['id_verified'] !== 1) {
            throw new Exception("ID not verified");
            } 

        if ((int)$user['is_banned'] === 1) {
            throw new Exception("Your account is restricted from making transactions.");
        }

        if ($user['ngn_balance'] < $amount) {
            throw new Exception("Insufficient funds.");
        }

        // --- Verify user balance integrity ---
        $check = verifyUserBalance($user['id']);
        if (!$check['valid']) {
            throw new Exception("Something failed.");
        }

        // --- Debit wallet securely ---
        $debitQuery = $mysqli->prepare("
            UPDATE users 
            SET ngn_balance = ngn_balance - ?, total_spent = total_spent + ? 
            WHERE id = ? AND ngn_balance >= ?
        ");
        $debitQuery->bind_param("ddid", $amount, $amount, $user['id'], $amount);
        if (!$debitQuery->execute() || $debitQuery->affected_rows === 0) {
            throw new Exception("Failed to debit wallet.");
        }
        $debitQuery->close();

        // --- Insert transaction record (status=processing) ---
        $status  = 'processing';
        $title   = 'Electricity purchase';
        $desc    = 'Electricity bill payment';
        $time    = time();
        $ref     = 'N/A';
        $our_ref = $request_id;
       
        $userId  = $user['id'];

        $insertTxn = $mysqli->prepare("
            INSERT INTO transactions 
            (flow, currency, title, description, date, amount, status, our_ref, email, user_id, type)
            VALUES ('out', 'NGN', ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");
        $insertTxn->bind_param(
            "sssdsssis",
            $title,
            $desc,
            $time,
            $amount,
            $status,
            $our_ref,
            $email,
            $userId,
            $type
        );
        $insertTxn->execute();
        $insertTxn->close();

        // --- Commit debit before VTpass call ---
        $mysqli->commit();
        // --- Call VTpass API ---
        $response = $client->request('POST', $vtpBase . 'pay', [
            'headers' => [
                'Content-Type' => 'application/json',
                'secret-key'   => $vtpSecKey,
                'api-key'      => $vtpApiKey,
            ],
            'body' => json_encode([
                "serviceID"      => $serviceID,
                "amount"         => $amount,
                "request_id"     => $request_id,
                "billersCode"    => $billersCode,
                "variation_code" => $variation_code,
                "phone"          => $phone,
            ]),
        ]);

        // --- Handle timeout or no response ---
        if (!$response || !$response->getBody()) {
            $pend = $mysqli->prepare("INSERT INTO pending_tx (tx_id, email, amount, type)
                                      VALUES (?, ?, ?, 'electricity')");
            $pend->bind_param("ssd", $request_id, $email, $amount);
            $pend->execute();
            $pend->close();
            releaseActionLock($tId, $type);
            echo json_encode(['success' => false, 'message' => 'Provider timeout — marked pending.']);
            exit;
        }

        $res = json_decode($response->getBody(), true);

        // --- If successful or chargeable ---
        if (isset($res['code']) && !in_array($res['code'], $nonChargeableFailureCodes)) {
            $ref = $res['content']['transactions']['transactionId'] ?? 'N/A';
            $ref = sanitizeInput($ref);
            $payloadJson = json_encode($res);

            $transactionQuery = $mysqli->prepare("
                UPDATE transactions 
                SET status = 'pending', payload = ?, ref = ? 
                WHERE our_ref = ?
            ");
            $transactionQuery->bind_param("sss", $payloadJson, $ref, $request_id);
            if (!$transactionQuery->execute()) {
                throw new Exception("Transaction record failed: " . $transactionQuery->error);
            }
            $transactionQuery->close();

            // Add to pending_tx
            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'electricity')
            ");
            $pendQuery->bind_param("ssd", $request_id, $email, $amount);
            $pendQuery->execute();
            $pendQuery->close();
        releaseActionLock($tId, $type);
payRef($tId);
   applyCashback(
    $tId,
    $our_ref,
    $amount
 );

            echo json_encode([
                'success' => true,
                'message' => $res['content']['transactions']['status'] ?? 'Electricity purchase successful.', 'data' => $res
            ]);
        } else {
            // --- If failed or nonchargeable ---
            $transactionQuery = $mysqli->prepare("UPDATE transactions SET status= 'Failed' WHERE our_ref=?");
            $transactionQuery->bind_param("s", $request_id);
            $transactionQuery->execute();
            $transactionQuery->close();

            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'electricity')
            ");
            $pendQuery->bind_param("ssd", $request_id, $email, $amount);
            $pendQuery->execute();
            $pendQuery->close();
        releaseActionLock($tId, $type);

            echo json_encode([
                'success' => false,
                'message' => $res['response_description'] ?? 'Electricity top-up failed.',
            ]);
        }

    } catch (Exception $e) {
        $mysqli->rollback();
                releaseActionLock($tId, $type);

        $error = parseError($e);

        echo json_encode([
            'success' => false,
            'message' => 'Transaction error: ' . $error
        ]);
    }

    break;


   case "vtp_pay_education":
    $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
    $serviceID      = sanitizeInput($_POST['serviceID']);
    $amount         = (float)sanitizeInput($_POST['amount']);
    $request_id     = sanitizeInput($_POST['request_id']);
    $billersCode    = sanitizeInput($_POST['billersCode']);
    $variation_code = sanitizeInput($_POST['variation_code']);
    $phone          = sanitizeInput($_POST['phone']);
    $quantity       = (int)sanitizeInput($_POST['quantity']);
    if ($quantity <= 0 || $quantity > 10) {
    exit(json_encode([
        'success' => false,
        'message' => 'Invalid quantity. Min 1, max 10'
    ]));
}

    $total          = $amount * $quantity;
 $userFromToken = getUserInfo($token);
$email = $userFromToken['email'];
$tId = $userFromToken['id'];

$type    = 'education';
  if (!acquireActionLock($tId, $type)) {
    exit(json_encode([
        'success' => false,
        'message' => 'Too many requests. cool down.'
    ]));
}
    try {
        // --- Begin transaction ---
        $mysqli->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);

        if ($total < 100) {
            throw new Exception("Invalid amount.");
        }

        // --- Prevent duplicate request IDs ---
        $dup = $mysqli->prepare("SELECT id FROM transactions WHERE our_ref = ? LIMIT 1");
        $dup->bind_param("s", $request_id);
        $dup->execute();
        if ($dup->get_result()->num_rows > 0) {
            throw new Exception("Duplicate request ID.");
        }
        $dup->close();

        // --- Lock user row to prevent concurrent balance modification ---
        $stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ? FOR UPDATE");
        $stmt->bind_param("s", $email);
        $stmt->execute();
        $result = $stmt->get_result();
        $user = $result->fetch_assoc();
        $stmt->close();

        if (!$user) {
            throw new Exception("User not found.");
        }

         if ($user['id_verified'] !== 1) {
            throw new Exception("ID not verified");
            } 

        if ((int)$user['is_banned'] === 1) {
            throw new Exception("Your account is restricted from making transactions.");
        }

        if ($user['ngn_balance'] < $total) {
            throw new Exception("Insufficient funds.");
        }

        // --- Verify balance integrity before debit ---
        $check = verifyUserBalance($user['id']);
        if (!$check['valid']) {
            throw new Exception("Something failed.");
        }

        // --- Debit wallet securely ---
        $debitQuery = $mysqli->prepare("
            UPDATE users 
            SET ngn_balance = ngn_balance - ?, total_spent = total_spent + ? 
            WHERE id = ? AND ngn_balance >= ?
        ");
        $debitQuery->bind_param("ddid", $total, $total, $user['id'], $total);
        if (!$debitQuery->execute() || $debitQuery->affected_rows === 0) {
            throw new Exception("Failed to debit wallet.");
        }
        $debitQuery->close();

        // --- Insert initial transaction record ---
        $status  = 'processing';
        $title   = 'Education Payment';
        $desc    = 'Exam / Educational Service Payment';
        $time    = time();
        $ref     = 'N/A';
        $our_ref = $request_id;
        $userId  = $user['id'];

        $insertTxn = $mysqli->prepare("
            INSERT INTO transactions 
            (flow, currency, title, description, date, amount, status, our_ref, email, user_id, type)
            VALUES ('out', 'NGN', ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");
        $insertTxn->bind_param(
            "sssdsssis",
            $title,
            $desc,
            $time,
            $total,
            $status,
            $our_ref,
            $email,
            $userId,
            $type
        );
        $insertTxn->execute();
        $insertTxn->close();

        // --- Commit debit before calling VTpass API ---
        $mysqli->commit();

        // --- Call VTpass API ---
        $response = $client->request('POST', $vtpBase . 'pay', [
            'headers' => [
                'Content-Type' => 'application/json',
                'secret-key'   => $vtpSecKey,
                'api-key'      => $vtpApiKey,
            ],
            'body' => json_encode([
                "serviceID"      => $serviceID,
                "amount"         => $amount,
                "request_id"     => $request_id,
                "billersCode"    => $billersCode,
                "variation_code" => $variation_code,
                "phone"          => $phone,
                "quantity"       => $quantity,
            ]),
        ]);

        // --- Timeout / null response handling ---
        if (!$response || !$response->getBody()) {
            $pend = $mysqli->prepare("INSERT INTO pending_tx (tx_id, email, amount, type)
                                      VALUES (?, ?, ?, 'education')");
            $pend->bind_param("ssd", $request_id, $email, $total);
            $pend->execute();
            $pend->close();
             releaseActionLock($tId, $type);
            echo json_encode(['success' => false, 'message' => 'Provider timeout — marked pending.']);
            exit;
        }

        $res = json_decode($response->getBody(), true);

        // --- Handle success or chargeable response ---
        if (isset($res['code']) && !in_array($res['code'], $nonChargeableFailureCodes)) {
            $ref = $res['content']['transactions']['transactionId'] ?? 'N/A';
            $ref = sanitizeInput($ref);
            $payloadJson = json_encode($res);

            $transactionQuery = $mysqli->prepare("
                UPDATE transactions 
                SET status = 'pending', payload = ?, ref = ? 
                WHERE our_ref = ?
            ");
            $transactionQuery->bind_param("sss", $payloadJson, $ref, $request_id);
            if (!$transactionQuery->execute()) {
                throw new Exception("Transaction record failed: " . $transactionQuery->error);
            }
            $transactionQuery->close();

            // Record to pending_tx
            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'education')
            ");
            $pendQuery->bind_param("ssd", $request_id, $email, $total);
            $pendQuery->execute();
            $pendQuery->close();
 releaseActionLock($tId, $type);
 payRef($tId);
 applyCashback(
    $tId,
    $our_ref,
    $total
 );
            echo json_encode([
                'success' => true,
                'message' => $res['content']['transactions']['status'] ?? 'Education payment successful.',
                'data' => $res
            ]);
        } else {
            // --- Failed or nonchargeable ---
            $transactionQuery = $mysqli->prepare("UPDATE transactions SET status= 'Failed' WHERE our_ref=?");
            $transactionQuery->bind_param("s", $request_id);
            $transactionQuery->execute();
            $transactionQuery->close();

            $pendQuery = $mysqli->prepare("
                INSERT INTO pending_tx 
                (tx_id, email, amount, type) 
                VALUES (?, ?, ?, 'education')
            ");
            $pendQuery->bind_param("ssd", $request_id, $email, $total);
            $pendQuery->execute();
            $pendQuery->close();
 releaseActionLock($tId, $type);
            echo json_encode([
                'success' => false,
                'message' => $res['response_description'] ?? 'Education payment failed.',
            ]);
        }

    } catch (Exception $e) {
        $mysqli->rollback();
         releaseActionLock($tId, $type);
        $error = parseError($e);

      
        echo json_encode([
            'success' => false,
            'message' => 'Transaction error: ' . $error
        ]);
    }

    break;

       

        case "verify_account_number":
            $code = sanitizeInput($_POST['code']);
            $account = sanitizeInput($_POST['account']);
        
            try {
                $q = $client->request('POST', $quidaxBase . 'banks/verify_account', [
                    'body' => json_encode([
                        'fund_uid' => $account,
                        'fund_uid2' => $code,
                        'currency' => 'ngn',
                    ]),
                    'headers' => [
                        'Authorization' => 'Bearer ' . $qsk,
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json',
                    ],
                ]);
        
                $res = json_decode($q->getBody(), true);
                                        error_log(
                                    json_encode([
                        'fund_uid' => $account,
                        'fund_uid2' => $code,
                        'currency' => 'ngn',
                    ])        
                                        );

                    //    error_log("ACCT". $q->getBody());

                if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
                    echo json_encode([
                        'success' => true,
                        'message' => $res['data']['account_name']
                    ]);
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => $res['message'] ?? 'Account verification failed.'
                    ]);
                }
        
            } catch (Exception $e) {
                $error = parseError($e);
                error_log("VERIFICATION". $e->getMessage());
             error_log(
                                    json_encode([
                        'fund_uid' => $account,
                        'fund_uid2' => $code,
                        'currency' => 'ngn',
                    ])        
                                        );
                echo json_encode([
                    'success' => false,
                    'message' => 'Request failed: ' . $error
                ]);

            }
        
            break;
        
              case "queue_broadcast":
     $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    $user = getUserInfo($token); 
     if (!$user || (int)$user['is_admin'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access..']);
        exit;
    }            

    $type = $_POST['type'] ?? '';
    $subject = $_POST['subject'] ?? '';
    $message = $_POST['message'] ?? '';

    if (!$type || !$message) {
        echo json_encode(["success" => false, "message" => "Missing fields"]);
        exit;
    }

    $stmt = $mysqli->prepare("
        INSERT INTO broadcasts (type, subject, message, created_at)
        VALUES (?, ?, ?, ?)
    ");
    $time = time();
    $stmt->bind_param("sssi", $type, $subject, $message, $time);

    if ($stmt->execute()) {
        echo json_encode(["success" => true, "message" => "Broadcast queued successfully"]);
    } else {
        echo json_encode(["success" => false, "message" => "Failed to queue broadcast"]);
    }

    break;


        case 'get_quidax_banklist':

            try {
                $allBanks = [];
                $nextPage = null;
        
                do {
                    $url = $quidaxBase . 'banks?per_page=100';
                    if ($nextPage !== null) {
                        $url .= '&page=' . $nextPage;
                    }
        
                    $q = $client->request('GET', $url, [
                        'headers' => [
                            'Authorization' => 'Bearer ' . $qsk,
                            'Accept' => 'application/json',
                            'Content-Type' => 'application/json',
                        ],
                    ]);
        
                    $res = json_decode($q->getBody(), true);
        
                    if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
                        $allBanks = array_merge($allBanks, $res['data']);
                    } else {
                        echo json_encode([
                            'success' => false,
                            'message' => 'Error: ' . ($res['message'] ?? 'Unknown error')
                        ]);
                        exit;
                    }
        
                    // Get the next page from response headers
                    $nextPage = $q->getHeaderLine('X-Next-Page');
                } while (!empty($nextPage));
        
                echo json_encode([
                    'success' => true,
                    'message' => 'Bank list retrieved',
                    'banks' => $allBanks
                ]);
        
            } catch (Exception $e) {
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
        
            break;

           

         case 'user_signup':

  $fname = sanitizeInput($_POST['first_name'] ?? '', 'string');
    $lname = sanitizeInput($_POST['last_name'] ?? '', 'string');
    $email = sanitizeInput($_POST['email'] ?? '', 'email');
    $pass  = $_POST['pass'] ?? '';
    $pass2 = $_POST['pass2'] ?? '';

    $ref = isset($_POST['ref_by']) && $_POST['ref_by'] !== ''
        ? sanitizeInput($_POST['ref_by'], 'string')
        : null;

    $phone = isset($_POST['phone']) && $_POST['phone'] !== ''
        ? sanitizeInput($_POST['phone'], 'string')
        : null;

    $time = time();
    $code = rand(1000, 9999);
    $refCode = strtoupper(bin2hex(random_bytes(3)));

    // ===== VALIDATION =====
    if (!$fname || !$lname) {
        echo json_encode(['success' => false, 'message' => 'Your first and last name are required.']);
        exit;
    }

    if (!isValidEmail($email)) {
        echo json_encode(['success' => false, 'message' => 'Invalid email address.']);
        exit;
    }

    if (checkExistVal('users', 'email', $email)) {
        echo json_encode(['success' => false, 'message' => 'This email is already registered.']);
        exit;
    }

    if ($phone !== null && strlen($phone) < 11) {
        echo json_encode(['success' => false, 'message' => 'Phone number is not valid.']);
        exit;
    }

    if ($phone !== null && checkExistVal('users', 'phone', $phone)) {
        echo json_encode(['success' => false, 'message' => 'This phone number is already registered.']);
        exit;
    }

    if ($pass !== $pass2) {
        echo json_encode(['success' => false, 'message' => 'The passwords do not match.']);
        exit;
    }

    if (strlen($pass) < 8) {
        echo json_encode(['success' => false, 'message' => 'Password must be at least 8 characters long.']);
        exit;
    }

    if ($ref !== null) {
        $r = getUserInfo($ref);
        if (empty($r)) {
            echo json_encode(['success' => false, 'message' => 'Referrer not found. Provide a valid ref code or remove it.']);
            exit;
        }
    }

    $hashed_pass = password_hash($pass, PASSWORD_DEFAULT);

    // ===== INSERT USER =====
    $stmt = $mysqli->prepare("
        INSERT INTO users 
        (ref_code, verify_token, reg_date, first_name, last_name, email, password, ref_by, phone) 
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    ");

    if (!$stmt) {
        error_log("Signup SQL Error: " . $mysqli->error);
        echo json_encode(['success' => false, 'message' => 'Unable to create account at the moment.']);
        exit;
    }

    // ✅ SINGLE bind_param (NULL-safe)
    $stmt->bind_param(
        "siissssss",
        $refCode,
        $code,
        $time,
        $fname,
        $lname,
        $email,
        $hashed_pass,
        $ref,
        $phone
    );
    
    if ($stmt->execute()) {
   
             //SUBSCRIBE
                       

$json = [
  "email" => $email,
  "firstname" => $fname,
  "lastname" => $lname,
  "groups" => ["bYqEYY"],
  "phone" => $phone,
  "trigger_automation" => false
];

$response = $client->post(
    'https://api.sender.net/v2/subscribers',
    [
        'headers' => [
            'Authorization' => 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiMTRlZDFkYjY2Zjg1NmIwOWFlMzYxOGNkYzVjNTUwOGQwODlkNTE3MGM5OWJlYTg5YmE1NmE3OTUwMGZkNjc4ZmU1NmE3YTIzNTAyMDViNWQiLCJpYXQiOiIxNzQ2OTIxMjAxLjQyMzI0NyIsIm5iZiI6IjE3NDY5MjEyMDEuNDIzMjQ5IiwiZXhwIjoiNDkwMDUyMTIwMS40MjIwNzAiLCJzdWIiOiI5NzY2OTYiLCJzY29wZXMiOltdfQ.MZEO6oE_khxhJgbF8Wnjt2tpKOPfOcyHSGxDHXpXKrLtvk6j6HTr8qMCiYe0ECDwYf5D9Rkj7fBQKDd1Vg7PAw',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => $json
    ]
);
$body = $response->getBody()->getContents();
$data = json_decode($body, true);

        // SEND VERIFICATION EMAIL
        $app = $set['app_name'];
        $subject = "Welcome to $app – Verify Your Email";

        $html = "
            <div style='font-family:Arial,sans-serif;max-width:600px;margin:auto;padding:20px;background:#f9f9f9;border-radius:8px;'>
                <h2 style='color:#1584d4;'>Welcome to {$app}, {$fname}!</h2>
                <p>Your verification code is:</p>
                <h1 style='color:#333;background:#e3f2fd;padding:10px;border-radius:6px;text-align:center;'>
                    {$code}
                </h1>
                <p>Please enter this code in the app to verify your account.</p>
                <hr>
                <p style='font-size:13px;color:#666;'>© " . date('Y') . " {$app}. All rights reserved.</p>
            </div>
        ";

        sendMail($email, $subject, $html);

        echo json_encode([
            'success' => true,
            'message' => 'Account created successfully. Please check your email (inbox or spam) for a verification code to verify and complete your registration..',
            'code'   => $code,
            'email'   => $email
        ]);

    } else {
        error_log("Signup Execution Error: " . $stmt->error);
        echo json_encode(['success' => false, 'message' => 'Unable to create account. Please try again later.']);
    }

    break; 
   
  case "generate_deposit_account":

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $u = getUserInfo($token);
    if (empty($u)) {
        echo json_encode(['success' => false, 'message' => 'User not found.']);
        exit;
    }

    if ((int)$u['is_banned'] === 1) {
        echo json_encode(['success' => false, 'message' => 'Your account is restricted.']);
        exit;
    }

    if (!empty($u['deposit_account'])) {
        echo json_encode(['success' => false, 'message' => 'Deposit account already exists.']);
        exit;
    }

    // Required fields check
    if (empty($u['bvn']) || empty($u['bvn_phone']) || empty($u['dob'])) {
        echo json_encode([
            'success' => false,
            'message' => 'Incomplete KYC data. Please complete verification.'
        ]);
        exit;
    }

    $access = getAccessToken();
    if (!$access) {
        echo json_encode(['success' => false, 'message' => 'Unable to authenticate provider.']);
        exit;
    }

    $extRef = 'swapeki_' . $u['id'] . '_' . time();

    try {
        $q = $client->request('POST', $shBase . 'accounts/v2/subaccount', [
            'json' => [
                'phoneNumber' => $u['bvn_phone'],
                'emailAddress' => $u['email'],
                'externalReference' => $extRef,
                'identityType' => 'BVN',
                'identityNumber' => $u['bvn'],
                'dateOfBirth' => $u['dob'],
                'booleanMatch' => true,
                'autoSweep' => true,
                'autoSweepDetails' => [
                    'schedule' => 'Instant',
                    'accountNumber' => '0119824672'
                ]
            ],
            'headers' => [
                'ClientID' => '67c6eb12de45b600247c8aa4',
                'Authorization' => 'Bearer ' . $access,
                'Accept' => 'application/json'
            ],
        ]);

        $res = json_decode($q->getBody(), true);

        if (empty($res['data']['accountNumber'])) {
            error_log('Deposit acct error response: ' . json_encode($res));
            echo json_encode([
                'success' => false,
                'message' => 'Account creation failed. Please try again.'
            ]);
            exit;
        }

        $account = $res['data']['accountNumber'];

        $query = $mysqli->prepare("
            UPDATE users
            SET deposit_account = ?
            WHERE id = ? AND deposit_account IS NULL
        ");
        $query->bind_param("si", $account, $u['id']);

        if (!$query->execute() || $query->affected_rows !== 1) {
            echo json_encode([
                'success' => false,
                'message' => 'Account already exists or could not be saved.'
            ]);
            exit;
        }

        echo json_encode([
            'success' => true,
            'message' => 'Your deposit account has been generated successfully.'
        ]);
        exit;

    } catch (\Throwable $e) {
        error_log('Deposit account error: ' . $e->getMessage());

        echo json_encode([
            'success' => false,
            'message' => parseError($e)
        ]);
        exit;
    }

    break;

     
           case 'get_user_giftcards_sales':
            $id = sanitizeInput($_POST['id']);
           $data = getArray('gift_card_sales', "user_id = '$id'", 'id DESC');

        
            if (isset($data['error'])) {
                echo json_encode([
                    'success' => false,
                    'data' => $data
                ]);
            } else {
                echo json_encode([
                    'success' => true,
                    'data' => $data
                ]);
            }
            break;

         case 'sell_giftcard':

    // -----------------------------
    // 1️⃣ AUTHENTICATION
    // -----------------------------
    $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]);
        exit;
    }

    $u = getUserInfo($token);

    if (!$u) {
        echo json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]);
        exit;
    }

    if ((int)$u['is_banned'] === 1) {
        echo json_encode([
            'success' => false,
            'message' => 'Your account is banned.'
        ]);
        exit;
    }

    $userId = (int)$u['id'];

    // -----------------------------
    // 2️⃣ VALIDATE INPUT (SERVER SIDE)
    // -----------------------------
    if (
        empty($_POST['cid']) ||
        empty($_POST['amount']) ||
        empty($_POST['card-type'])
    ) {
        echo json_encode([
            'success' => false,
            'message' => 'Missing required fields.'
        ]);
        exit;
    }

    $cardId   = (int)$_POST['cid'];
    $amount   = (float)$_POST['amount'];
    $cardType = sanitizeInput($_POST['card-type']);

    if ($amount <= 0) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid amount.'
        ]);
        exit;
    }

    if (!in_array($cardType, ['physical', 'ecode'], true)) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid card type.'
        ]);
        exit;
    }

    // -----------------------------
    // 3️⃣ FETCH CARD DATA FROM DB (TRUSTED)
    // -----------------------------
    $stmt = $mysqli->prepare("
        SELECT id, name, rate, logo
        FROM gift_cards
        WHERE id = ?
        LIMIT 1
    ");
    $stmt->bind_param("i", $cardId);
    $stmt->execute();
    $card = $stmt->get_result()->fetch_assoc();
    $stmt->close();

    if (!$card) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid or unsupported gift card.'
        ]);
        exit;
    }

    $cardName = $card['name'];
    $rate     = (float)$card['rate'];
 $cardLogo = $card['logo'];
    // 🔒 SERVER-CALCULATED TOTAL
    $total = round($amount * $rate, 2);

    // -----------------------------
    // 4️⃣ FILE UPLOAD (SECURE)
    // -----------------------------
    if (
        !isset($_FILES['card-image']) ||
        $_FILES['card-image']['error'] !== UPLOAD_ERR_OK
    ) {
        echo json_encode([
            'success' => false,
            'message' => 'Card image is required.'
        ]);
        exit;
    }

    $uploadDir = __DIR__ . '/uploads/giftcards/';
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }

    $tmpPath  = $_FILES['card-image']['tmp_name'];
    $fileSize = $_FILES['card-image']['size'];

    if ($fileSize > 5 * 1024 * 1024) {
        echo json_encode([
            'success' => false,
            'message' => 'Image exceeds 5MB limit.'
        ]);
        exit;
    }

    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime  = finfo_file($finfo, $tmpPath);
    finfo_close($finfo);

    $allowedMime = [
        'image/jpeg' => 'jpg',
        'image/png'  => 'png',
        'image/gif'  => 'gif'
    ];

    if (!isset($allowedMime[$mime])) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid image format.'
        ]);
        exit;
    }

    $newFileName = sprintf(
        'gc_%d_%s.%s',
        $userId,
        bin2hex(random_bytes(8)),
        $allowedMime[$mime]
    );

    $destPath =  'uploads/giftcards/' . $newFileName;

    if (!move_uploaded_file($tmpPath, $destPath)) {
        echo json_encode([
            'success' => false,
            'message' => 'Failed to upload image.'
        ]);
        exit;
    }

    // -----------------------------
    // 5️⃣ INSERT SALE (SAFE)
    // -----------------------------
    $time = time();
    $stmt = $mysqli->prepare("
        INSERT INTO gift_card_sales
        (created_at, user_id, card_id, name, image, amount, rate, total, type, status)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')
    ");
    $stmt->bind_param(
        "siissddds",
        $time,
        $userId,
        $cardId,
        $cardName,
        $destPath,
        $amount,
        $rate,
        $total,
        $cardType
    );

    if (!$stmt->execute()) {
        echo json_encode([
            'success' => false,
            'message' => 'Failed to submit gift card.'
        ]);
        exit;
    }

    $stmt->close();
// After successful INSERT into gift_card_sales

$subject = "New Gift Card Sale Submitted";

$body = "
    <p><strong>A new gift card sale has been submitted.</strong></p>
    <ul>
        <li><strong>User:</strong> {$u['first_name']} ({$u['email']})</li>
        <li><strong>User ID:</strong> {$userId}</li>
        <li><strong>Card:</strong> {$cardName}</li>
        <li><strong>Type:</strong> " . ucfirst($cardType) . "</li>
        <li><strong>USD Amount:</strong> {$amount}</li>
        <li><strong>Rate:</strong> ₦" . number_format($rate, 2) . "</li>
        <li><strong>Total (NGN):</strong> ₦" . number_format($total, 2) . "</li>
        <li><strong>Status:</strong> Pending Review</li>
        <li><strong>Time:</strong> " . date('Y-m-d H:i:s') . "</li>
        <li><strong>IP:</strong> " . ($_SERVER['REMOTE_ADDR'] ?? 'CLI') . "</li>
    </ul>
    <p>Please review and approve/reject from the admin dashboard.</p>
";

notifyAdmins($subject, $body);

    echo json_encode([
        'success' => true,
        'message' => 'Gift card submitted successfully. Processing started.'
    ]);
    break;

    

        case 'get_giftcards_by_category':
            $category = sanitizeInput($_POST['category']);
            $type = sanitizeInput($_POST['type']);

           $data = getArray('gift_cards', "category = '$category' AND type = '$type'", 'name ASC');

        
            if (isset($data['error'])) {
                echo json_encode([
                    'success' => false,
                    'data' => $data
                ]);
            } else {
                echo json_encode([
                    'success' => true,
                    'data' => $data
                ]);
            }
            break;
        


        case 'get_giftcard_categories':
            $data = getArray('gift_card_categories', '', 'name ASC');
        
            if (isset($data['error'])) {
                echo json_encode([
                    'success' => false,
                    'data' => $data
                ]);
            } else {
                echo json_encode([
                    'success' => true,
                    'data' => $data
                ]);
            }
            break;
        

              case 'send_crypto':
                $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
                $id = sanitizeInput($_POST['id']);
                $currency = strtolower(sanitizeInput($_POST['currency']));
                $amount = sanitizeInput($_POST['amount']);
                $wallet = sanitizeInput($_POST['wallet']);
                $chain = strtolower(sanitizeInput($_POST['chain']));
            
                $u = getUserInfo($token);
            
                if (empty($u)) {
                    echo json_encode([
                        'success' => false,
                        'message' => 'User not found.'
                    ]);
                    exit;
                }

                  if ($u['is_banned'] === 1) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Your account is banned.'
                ]);
                exit;
            }
            
                if ($u['quidax_id'] === null) {
                    echo json_encode([
                        'success' => false,
                        'message' => 'Please create a trading account first.'
                    ]);
                    exit;
                }
            
                $q_id = $u['quidax_id'];
                $reference = uniqid() . '=' . $u['email'];
            
                try {
                    $q = $client->request('POST', $quidaxBase . 'users/' . $q_id . '/withdraws', [
                        'body' => json_encode([
                            'currency' => $currency,
                            'amount' => $amount,
                            'fund_uid' => $wallet,
                            'network' => $chain,
                            'transaction_note' => 'send',
                            'reference' => $reference,
                            'narration' => 'send'
                        ]),
                        'headers' => [
                            'Authorization' => 'Bearer ' . $qsk,
                            'Accept' => 'application/json',
                            'Content-Type' => 'application/json',
                        ],
                    ]);
            
                    $res = json_decode($q->getBody(), true);
            
                    if ($res && isset($res['status']) && $res['status'] === 'success') {
                        echo json_encode([
                            'success' => true,
                            'message' => 'Your transaction is now being processed. We will email you about the status.'
                        ]);
                    } else {
                        echo json_encode([
                            'success' => false,
                            'message' => 'Withdrawal failed: ' . ($res['message'] ?? 'Unknown error')
                        ]);
                    }
                } catch (Exception $e) {
                $error = parseError($e);

                    error_log("Crypto withdrawal error: " . $e->getMessage());
                    echo json_encode([
                        'success' => false,
                        'message' => 'Unable to process transaction right now. '.$error
                    ]);
                }
            
                break;
            

        case 'validate_wallet_address':
            $currency = sanitizeInput($_POST['currency']);
            $wallet = sanitizeInput($_POST['wallet']);
            try {
$q = $client->request('GET', $quidaxBase .$currency. '/'.$wallet.'/validate_address', [
    'headers' => [
        'Authorization' => 'Bearer ' . $qsk,
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
]);

$res = json_decode($q->getBody(), true);
if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
    echo json_encode([
        'success' => true,
        'message' => $res['data']['valid']
    ]);
}
 else {
 echo json_encode([
'success' => false,
'message' => 'Validate error: '.$res['message']
                    ]);
                }
            } catch (Exception $e) {
            
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
            break;


       case 'get_quidax_single_wallet':
            $id = sanitizeInput($_POST['id']);
             $t = getBearerToken();
            $token = sanitizeInput($_POST['token']);
            $chain = strtoupper(sanitizeInput($_POST['chain']));

             if (!$t || !verifyAuthToken($t)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
            $u = getUserInfo($t);
        
            if (empty($u)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            if ($u['quidax_id'] === null) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Please create a trading account first.'
                ]);
                exit;
            }
            $q_id = $u['quidax_id'];
         
            try {
$q = $client->request('GET', $quidaxBase . 'users/'.$q_id.'/wallets/'.$token, [
    'headers' => [
        'Authorization' => 'Bearer ' . $qsk,
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
]);

$res = json_decode($q->getBody(), true);
if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
    $q2 = $client->request('GET', $quidaxBase . 'fee', [
        'query' => ['currency' => $token, 'network' => strtolower($chain) ], 
        'headers' => [
            'Authorization' => 'Bearer ' . $qsk,
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
        ],
    ]);
    $res2 = json_decode($q2->getBody(), true);

    $res['data']['chain'] = $chain;
   // error_log("FEE.". json_encode($res2));
    if (isset($res2['status']) && $res2['status'] === 'success' && !empty($res2['data'])) {
        $res['data']['fee'] = $res2['data']['fee'];
    }
    echo json_encode([
        'success' => true,
        'message' => $res['data']
    ]);
}
 else {
 echo json_encode([
'success' => false,
'message' => 'No token records found.'
                    ]);
                }
            } catch (Exception $e) {
            
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
            break;

    case 'confirm_instant_order':

    // 🔐 AUTH
    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    // 👤 AUTH USER (SOURCE OF TRUTH)
    $user = getUserInfo($token);
    if (!$user) {
        exit(json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]));
    }

    if ((int)$user['is_banned'] === 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Account restricted.'
        ]));
    }

    // 🔒 ACTION LOCK (ANTI DOUBLE CONFIRM)
    if (!acquireActionLock($user['id'], 'instant_trade')) {
        exit(json_encode([
            'success' => false,
            'message' => 'Too many requests. Please wait.'
        ]));
    }

    // 📥 INPUT (CLIENT DATA – VALIDATED)
    $orderId  = sanitizeInput($_POST['order_id'] ?? '');
    $type     = sanitizeInput($_POST['type'] ?? '');
    $amount   = (float) sanitizeInput($_POST['amount'] ?? 0);
    $receive  = (float) sanitizeInput($_POST['receive'] ?? 0);
    $currency = strtolower(sanitizeInput($_POST['currency'] ?? ''));
    $wallet   = sanitizeInput($_POST['wallet'] ?? '');

    if (!$orderId || !$type || $amount <= 0 || !$currency) {
        releaseActionLock($user['id'], 'instant_trade');
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request data.'
        ]));
    }

    // 🧠 TYPE VALIDATION
    if (!in_array($type, ['buy', 'sell'], true)) {
        releaseActionLock($user['id'], 'instant_trade');
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid trade type.'
        ]));
    }

    // 🛡️ SELL LIMIT ENFORCEMENT (SERVER-SIDE)
if ($type === 'sell') {

    $sellAmount = (float)$receive; // to_amount = naira user receives
    $kycLevel = (int)$user['kyc'];

    // Minimum ₦5,000
    if ($sellAmount < 5000) {
        releaseActionLock($user['id'], 'instant_trade');
        exit(json_encode([
            'success' => false,
            'message' => 'Minimum crypto to sell must be 5000 Naira and above.'
        ]));
    }

    // KYC 0 → max ₦2M
    if ($kycLevel === 0 && $sellAmount > 2000000) {
        releaseActionLock($user['id'], 'instant_trade');
        exit(json_encode([
            'success' => false,
            'message' => 'Maximum crypto to sell must not be more than 2 million Naira.'
        ]));
    }

    // KYC 1+ → max ₦10M
    if ($kycLevel >= 1 && $sellAmount > 10000000) {
        releaseActionLock($user['id'], 'instant_trade');
        exit(json_encode([
            'success' => false,
            'message' => 'Maximum crypto to sell must not be more than 10 million Naira.'
        ]));
    }
}

    $time = time();
    $email = $user['email'];
    $quidaxUserId = $type === 'buy' ? 'me' : $user['quidax_id'];

    try {

        $mysqli->begin_transaction();

        // 🚫 PREVENT DUPLICATE CONFIRM
        $dup = $mysqli->prepare("
            SELECT id FROM trades WHERE trade_id = ? LIMIT 1
        ");
        $dup->bind_param("s", $orderId);
        $dup->execute();
        if ($dup->get_result()->num_rows > 0) {
            throw new Exception("Order already confirmed.");
        }
        $dup->close();

        // 🔒 LOCK USER BALANCE
        if ($type === 'buy') {

            $stmt = $mysqli->prepare("
                SELECT ngn_balance 
                FROM users 
                WHERE id = ? 
                FOR UPDATE
            ");
            $stmt->bind_param("i", $user['id']);
            $stmt->execute();
            $stmt->bind_result($balance);
            $stmt->fetch();
            $stmt->close();

            if ($balance < $amount) {
                throw new Exception("Insufficient funds.");
            }

            // 💸 DEBIT USER
            $stmt = $mysqli->prepare("
                UPDATE users 
                SET ngn_balance = ngn_balance - ? 
                WHERE id = ? AND ngn_balance >= ?
            ");
            $stmt->bind_param("did", $amount, $user['id'], $amount);
            if (!$stmt->execute() || $stmt->affected_rows === 0) {
                throw new Exception("Debit failed.");
            }
            $stmt->close();
        }
$cl = strtoupper($currency);
        // 🧾 LOG TRADE (PROCESSING)
        $stmt = $mysqli->prepare("
            INSERT INTO trades 
            (trade_id, type, currency, receive, email, wallet, paid_amount, status, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, 'processing', ?)
        ");
        $stmt->bind_param(
            "sssdssdi",
            $orderId,
            $type,
            $cl,
            $receive,
            $email,
            $wallet,
            $amount,
            $time
        );
        if (!$stmt->execute()) {
            throw new Exception("Failed to log trade.");
        }
        $stmt->close();

        $mysqli->commit();

        // 🌐 EXTERNAL API (AFTER DEBIT)
        $api = $client->request(
            'POST',
            $quidaxBase . "users/{$quidaxUserId}/swap_quotation/{$orderId}/confirm",
            [
                'headers' => [
                    'Authorization' => 'Bearer ' . $qsk,
                    'Accept'        => 'application/json',
                    'Content-Type'  => 'application/json',
                ],
                'timeout' => 30
            ]
        );

        $res = json_decode($api->getBody(), true);

        if (
            !isset($res['status']) ||
            $res['status'] !== 'success' ||
            empty($res['data']) ||
            $res['data']['status'] !== 'initiated'
        ) {
            throw new Exception($res['message'] ?? 'Trade confirmation failed.');
        }

        // ✅ UPDATE STATUS
        $stmt = $mysqli->prepare("
            UPDATE trades SET status = 'initiated' WHERE trade_id = ?
        ");
        $stmt->bind_param("s", $orderId);
        $stmt->execute();
        $stmt->close();

        releaseActionLock($user['id'], 'instant_trade');

        echo json_encode([
            'success' => true,
            'message' => 'Transaction initiated successfully. You will be notified once completed.'
        ]);

    } catch (Exception $e) {
  $error = parseError($e);
        $mysqli->rollback();
        releaseActionLock($user['id'], 'instant_trade');

        error_log("INSTANT TRADE ERROR: " . $e->getMessage());

        echo json_encode([
            'success' => false,
            'message' => 'Request failed: ' . $error
        ]);
    }

    break;

  case "report_low_balance":

    $balance    = (float) sanitizeInput($_POST['balance']);
    $amount     = (float) sanitizeInput($_POST['amount']);
    $platform   = sanitizeInput($_POST['platform']);
    $userEmail  = sanitizeInput($_POST['userEmail'], 'email');

    if (!$platform || !$userEmail || $amount <= 0) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request data.'
        ]));
    }

    // 🔎 Fetch all admin emails
    $stmt = $mysqli->prepare("
        SELECT email 
        FROM users 
        WHERE is_admin = 1 AND is_banned = 0
    ");
    $stmt->execute();
    $res = $stmt->get_result();

    if (!$res || $res->num_rows === 0) {
        error_log("Low balance alert: No admin users found.");
        exit;
    }

    // 📧 Email content
    $subject = "⚠️ Low {$platform} Balance Alert";
    $message  = "<p>Hello Admin,</p>";
    $message .= "<p>A user (<strong>{$userEmail}</strong>) encountered an error while attempting to transact ";
    $message .= "<strong>₦" . number_format($amount, 2) . "</strong>.</p>";
    $message .= "<p><strong>Platform:</strong> {$platform}</p>";
    $message .= "<p><strong>Current Balance:</strong> ₦" . number_format($balance, 2) . "</p>";
    $message .= "<p>Please fund the <strong>{$platform}</strong> wallet as soon as possible.</p>";
    $message .= "<p>Regards,<br>System Monitor</p>";

    // 📤 Send to all admins
    while ($row = $res->fetch_assoc()) {
        if (!empty($row['email'])) {
            sendMail($row['email'], $subject, $message);
        }
    }

    $stmt->close();

    echo json_encode([
        'success' => true,
        'message' => 'Low balance alert sent to all admins.'
    ]);

    break;


        case 'get_quidax_admin_wallet':
                $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

            try {
$q = $client->request('GET', $quidaxBase . 'users/me/wallets/ngn', [
    'headers' => [
        'Authorization' => 'Bearer ' . $qsk,
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
]);

$res = json_decode($q->getBody(), true);
if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
    echo json_encode([
        'success' => true,
        'message' => $res['data']
    ]);
}
 else {
 echo json_encode([
'success' => false,
'message' => 'No NGN BALANCE found.'
                    ]);
                }
            } catch (Exception $e) {
            
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
            break;

 case 'get_usdt_ngn_rates':

    $set = getSettings();
    $s = $set[0];

    try {

        /*
        |--------------------------------------------------------------------------
        | BUY RATE (NGN → USDT)
        |--------------------------------------------------------------------------
        | How much NGN user pays to get 1 USDT
        */
        $buyQuote = $client->request(
            'POST',
            $quidaxBase . 'users/me/swap_quotation',
            [
                'headers' => [
                    'Authorization' => 'Bearer ' . $qsk,
                    'Accept' => 'application/json',
                    'Content-Type' => 'application/json',
                ],
                'body' => json_encode([
                    'from_currency' => 'ngn',
                    'to_currency'   => 'usdt',
                    'from_amount'   => 1000, // stable base amount
                ]),
            ]
        );

        $buyRes = json_decode($buyQuote->getBody(), true);

        if (!isset($buyRes['status']) || $buyRes['status'] !== 'success') {
            throw new Exception('Failed to fetch buy rate');
        }

        $marketBuyRate = $buyRes['data']['quoted_price'];
        $buyRate = $marketBuyRate + $s['quidax_buy_rate'];

        /*
        |--------------------------------------------------------------------------
        | SELL RATE (USDT → NGN)
        |--------------------------------------------------------------------------
        | How much NGN user gets for 1 USDT
        */
        $sellQuote = $client->request(
            'POST',
            $quidaxBase . 'users/me/swap_quotation',
            [
                'headers' => [
                    'Authorization' => 'Bearer ' . $qsk,
                    'Accept' => 'application/json',
                    'Content-Type' => 'application/json',
                ],
                'body' => json_encode([
                    'from_currency' => 'usdt',
                    'to_currency'   => 'ngn',
                    'from_amount'   => 1,
                ]),
            ]
        );

        $sellRes = json_decode($sellQuote->getBody(), true);

        if (!isset($sellRes['status']) || $sellRes['status'] !== 'success') {
            throw new Exception('Failed to fetch sell rate');
        }

        $marketSellRate = $sellRes['data']['quoted_price'];
        $sellRate = $marketSellRate - $s['quidax_sell_rate'];

        echo json_encode([
            'success' => true,
            'message' => [
                'buy' => [
                    'rate'        => round($buyRate, 2),
                    'market_rate' => round($marketBuyRate, 2),
                    'adjustment'  => $s['quidax_buy_rate'],
                ],
                'sell' => [
                    'rate'        => round($sellRate, 2),
                    'market_rate' => round($marketSellRate, 2),
                    'adjustment'  => $s['quidax_sell_rate'],
                ],
            ],
        ]);

    } catch (Exception $e) {
        echo json_encode([
            'success' => false,
            'message' => 'Rate fetch failed: ' . $e->getMessage(),
        ]);
    }

    break;
           

         case 'get_buy_order_quote':
            $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
                    'success' => false,
                    'message' => 'Invalid request session.'
                ]);
                exit;
    }
            $id = sanitizeInput($_POST['id'], 'int');
            $amount = sanitizeInput($_POST['amount'], 'float');
            $type = sanitizeInput($_POST['type']);
            $bid = sanitizeInput($_POST['bid']); //from currency
            $ask = sanitizeInput($_POST['ask']); //currency to buy
            $set = getSettings();
            $s = $set[0];
            $u = getUserInfo($token);
        
            if (empty($u)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            if ($u['quidax_id'] === null) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Please create a trading account first.'
                ]);
                exit;
            }
            if ($type === 'buy' && $u['ngn_balance'] < $amount) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Insufficient funds.'
                ]);
                exit;
            }
             $check = verifyUserBalance($u['id']);
if ($type === 'buy' && !$check['valid']) {
    echo json_encode([
        'success' => false,
        'message' => 'Balance verification failed.'
    ]);
    exit;
}
            if ($u['is_banned'] === 1) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Your account is banned.'
                ]);
                exit;
            }
            $q_id = $type === 'buy' ? 'me' : $u['quidax_id'];
            $res2 = null;

            try {
                $q = $client->request('POST', $quidaxBase . 'users/'.$q_id.'/swap_quotation', [
                    'body' => json_encode([
                        'from_currency' => $bid,
                        'to_currency' => $ask,
                        'from_amount' => $amount,
                    ]),
                    'headers' => [
                        'Authorization' => 'Bearer ' . $qsk,
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json',
                    ],
                ]);
        
                $res = json_decode($q->getBody(), true);
       
                if ($res && isset($res['status']) && $res['status'] === 'success') {
                    //GET USD FEE
                    if($bid !== 'usdt'){
                    $q2 = $client->request('POST', $quidaxBase . 'users/'.$q_id.'/swap_quotation', [
                        'body' => json_encode([
                            'from_currency' => $type === 'sell' ? $bid : 'ngn',
                            'to_currency' => 'usdt',
                            'from_amount' => $amount,
                        ]),
                        'headers' => [
                            'Authorization' => 'Bearer ' . $qsk,
                            'Accept' => 'application/json',
                            'Content-Type' => 'application/json',
                        ],
                    ]);
            
                    $res2 = json_decode($q2->getBody(), true);
                }
                    if ($res2 !== null && isset($res2['status']) && $res2['status'] === 'success' && $type === 'buy') {
                        $usdRate = isset($res2['data']['quoted_price']) ? $res2['data']['quoted_price'] + $s["quidax_buy_rate"] : 0;
$swapFeeNGN = isset($res2['data']['to_amount']) ? $res2['data']['to_amount'] * $s["quidax_buy_rate"] : 0;
                    }
                   else if ($res2 !== null && isset($res2['status']) && $res2['status'] === 'success' && $type === 'sell') {
                    $usdt = $res['data']['to_amount'] / $res2['data']['to_amount'];
                        $usdRate = $usdt - $s["quidax_sell_rate"];
$swapFeeNGN = isset($res2['data']['to_amount']) ? $res2['data']['to_amount'] * $s["quidax_sell_rate"] : 0;
                    }
                    else {
                        $usdRate = isset($res['data']['quoted_price']) ? $res['data']['quoted_price'] - $s["quidax_sell_rate"] : 0;
                        $swapFeeNGN = $amount * $s["quidax_sell_rate"];  
                    }
                    $swapFeeCrypto = $res['data']['quoted_price'] > 0 ? round($swapFeeNGN) / round($res['data']['quoted_price']) : 0;

                    $receive = isset($res['data']['to_amount']) 
                    ? max(0, $res['data']['to_amount'] - ($type === 'sell' ? $swapFeeNGN : $swapFeeCrypto))
                    : 0;
                  //  $res['data']['res_1'] = $res['data']['to_amount'];
                  //  $res['data']['res_2'] = $res2['data']['to_amount'];               
$res['data']['usd_rate'] = $usdRate;
$res['data']['swap_fee_ngn'] = $swapFeeNGN;
$res['data']['swap_fee_crypto'] = $swapFeeCrypto;
$res['data']['to_amount'] = $receive;
                      
                        echo json_encode([
                            'success' => true,
                            'message' => $res['data']
                        ]);
                    
                   
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => "Error: " . ($res['message'] ?? 'Unknown error occurred')
                    ]);
                }
            } catch (Exception $e) {
              //   error_log($e);
                 $error = parseError($e);
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $error
                ]);
            }
            break;

        case 'get_user_wallets':
              $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }
            $u = getUserInfo($token);
        
            if (empty($u)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            if ($u['quidax_id'] === null) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Please create a trading account first.'
                ]);
                exit;
            }
        
            $addresses = [];
            $q_id = $u['quidax_id'];
        
            try {
                $wallet_types = ['btc', 'usdt', 'eth', 'sol', 'bnb', 'usdc'];
                foreach ($wallet_types as $currency) {
                    $q = $client->request('GET', $quidaxBase . 'users/' . $q_id . '/wallets/' . $currency . '/addresses', [
                        'headers' => [
                            'Authorization' => 'Bearer ' . $qsk,
                            'Accept' => 'application/json',
                            'Content-Type' => 'application/json',
                        ],
                    ]);
        
                    $res = json_decode($q->getBody(), true);
        
                    if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
                        $addresses = array_merge($addresses, $res['data']); // Merge all addresses into a single array
                    }
                }
        
                if (!empty($addresses)) {
                    echo json_encode([
                        'success' => true,
                        'message' => $addresses
                    ]);
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => 'No wallet addresses found.'
                    ]);
                }
            } catch (Exception $e) {
         
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
        
            break;
        
/*
        case 'get_grouped_user_wallets':
              $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }
            $u = getUserInfo($token);
        
            if (empty($u)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            if ($u['quidax_id'] === null) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Please create a trading account first.'
                ]);
                exit;
            }
        
            $addresses = [];
            $q_id = $u['quidax_id'];
        
            try {
                $wallet_types = ['btc', 'usdt', 'eth', 'sol', 'usdc', 'bnb'];
                foreach ($wallet_types as $currency) {
                    $q = $client->request('GET', $quidaxBase . 'users/' . $q_id . '/wallets/' . $currency, [
                        'headers' => [
                            'Authorization' => 'Bearer ' . $qsk,
                            'Accept' => 'application/json',
                            'Content-Type' => 'application/json',
                        ],
                    ]);
        
                    $res = json_decode($q->getBody(), true);
        
                    if (isset($res['status']) && $res['status'] === 'success' && !empty($res['data'])) {
                        $addresses[$currency] = $res['data']; // Store separately for clarity
                    }
                }
        
                if (!empty($addresses)) {
                    echo json_encode([
                        'success' => true,
                        'message' => $addresses
                    ]);
                } else {
                 
                    echo json_encode([
                        'success' => false,
                        'message' => 'No wallet addresses found.'
                    ]);
                }
            } catch (Exception $e) {
            
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
        
            break;
            */

            case 'get_grouped_user_wallets':

    $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]);
        exit;
    }

    $u = getUserInfo($token);

    if (empty($u)) {
        echo json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]);
        exit;
    }

    if (empty($u['quidax_id'])) {
        echo json_encode([
            'success' => false,
            'message' => 'Please create a trading account first.'
        ]);
        exit;
    }

    $q_id = $u['quidax_id'];

    $wallet_types = ['btc', 'usdt', 'eth', 'sol', 'usdc', 'bnb'];

    $wallets = [];
    $totalNGN = 0;
    $totalUSD = 0;

    try {
        foreach ($wallet_types as $currency) {

            // 🔹 Fetch wallet
            $q = $client->request(
                'GET',
                $quidaxBase . 'users/' . $q_id . '/wallets/' . $currency,
                [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $qsk,
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json',
                    ],
                ]
            );

            $res = json_decode($q->getBody(), true);

            if (!isset($res['status']) || $res['status'] !== 'success' || empty($res['data'])) {
                continue;
            }

            $wallet = $res['data'];

            // 🔹 NGN equivalent (already provided by Quidax)
            $ngnValue = floatval($wallet['converted_balance'] ?? 0);

            // 🔹 USD equivalent
            if (in_array($currency, ['usdt', 'usdc'])) {
                $usdValue = floatval($wallet['balance']);
            } else {
                $usdValue = 0;

                if (floatval($wallet['balance']) > 0) {
                    try {
                        $q2 = $client->request(
                            'POST',
                            $quidaxBase . 'users/me/swap_quotation',
                            [
                                'body' => json_encode([
                                    'from_currency' => $currency,
                                    'to_currency'   => 'usdt',
                                    'from_amount'   => $wallet['balance'],
                                ]),
                                'headers' => [
                                    'Authorization' => 'Bearer ' . $qsk,
                                    'Accept' => 'application/json',
                                    'Content-Type' => 'application/json',
                                ],
                            ]
                        );

                        $res2 = json_decode($q2->getBody(), true);

                        if (isset($res2['status']) && $res2['status'] === 'success') {
                            $usdValue = floatval($res2['data']['to_amount'] ?? 0);
                        }
                    } catch (Exception $e) {
                      //  error_log("USD quote failed for {$currency}: " . $e->getMessage());
                    }
                }
            }

            // 🔹 Attach values
            $wallet['ngn_value'] = round($ngnValue, 2);
            $wallet['usd_value'] = round($usdValue, 6);

            // 🔹 Portfolio totals
            $totalNGN += $wallet['ngn_value'];
            $totalUSD += $wallet['usd_value'];

            $wallets[$currency] = $wallet;
        }

        if (empty($wallets)) {
            echo json_encode([
                'success' => false,
                'message' => 'No wallet addresses found.'
            ]);
            exit;
        }

        echo json_encode([
            'success' => true,
            'message' => [
                'wallets' => $wallets,
                'totals' => [
                    'ngn' => round($totalNGN, 2),
                    'usd' => round($totalUSD, 6),
                ]
            ]
        ]);

    } catch (Exception $e) {
        echo json_encode([
            'success' => false,
            'message' => 'Request failed: ' . $e->getMessage()
        ]);
    }

    break;

        
        case 'generate_wallet_address':
            $id = sanitizeInput($_POST['id']);
            $currency = sanitizeInput($_POST['currency']);
            $chain = sanitizeInput($_POST['chain']);
            $u = getUserInfo($id);
        
            if (empty($u)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            if ($u['quidax_id'] === null) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Please create a trading account first.'
                ]);
                exit;
            }
        
            $q_id = $u['quidax_id'];
        
            try {
                $q = $client->request('POST', $quidaxBase . 'users/' . $q_id . '/wallets/' . $currency . '/addresses', [
                    'query' => ['network' => $chain], 
                    'headers' => [
                        'Authorization' => 'Bearer ' . $qsk,
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json',
                    ],
                ]);
        
                $res = json_decode($q->getBody(), true);
        
                if ($res['status'] === 'success') {
                    echo json_encode([
                        'success' => true,
                        'message' => 'Your ' . $currency . ' wallet address is now being generated on the ' . $chain . ' chain. You will receive an email as soon as this process is completed.'
                    ]);
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => "Error: " . ($res['message'] ?? 'Unknown error occurred')
                    ]);
                }
            } catch (Exception $e) {
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
        
            break;
        

        case 'create_crypto_account':
            $id = sanitizeInput($_POST['id']);
            $u = getUserInfo($id);
        
            if (empty($u)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            $email = $u['email'];
            $fName = $u['first_name'];
            $lName = $u['last_name'];
            list($username, $domain) = explode('@', $email);

            $newEmail = $username .$id. '@swapeki.com';
            try {
                $q = $client->request('POST', $quidaxBase . 'users', [
                    'body' => json_encode([
                        'email' => $newEmail,
                        'first_name' => $fName,
                        'last_name' => $lName,
                    ]),
                    'headers' => [
                        'Authorization' => 'Bearer ' . $qsk,
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json',
                    ],
                ]);
        
                $res = json_decode($q->getBody(), true);
        
                if ($res['status'] === 'success') {
                    $quidaxId = $res['data']['id'];
        
                    $stmt = $mysqli->prepare("UPDATE users SET quidax_id = ?, quidax_email = ? WHERE id = ?");
                    $stmt->bind_param("ssi", $quidaxId, $newEmail, $id);
        
                    if ($stmt->execute()) {
                        echo json_encode([
                            'success' => true,
                            'message' => 'Your crypto trading account has been created.'
                        ]);
                    } else {
                        echo json_encode([
                            'success' => false, // FIXED: Changed from true to false
                            'message' => 'Your crypto trading account has been created, but we could not save your ID. Please contact support and ask them to save: ' . $quidaxId . ' as your crypto ID.'
                        ]);
                    }
        
                    $stmt->close();
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => "Error: " . ($res['message'] ?? 'Unknown error occurred')
                    ]);
                }
            } catch (Exception $e) {
                echo json_encode([
                    'success' => false,
                    'message' => "Request failed: " . $e->getMessage()
                ]);
            }
            break;
        

        case 'get_tx_details':
         
            $id = sanitizeInput($_POST['id']);
            
         
        
            try {
               
                $Query = $mysqli->prepare("SELECT * FROM transactions WHERE our_ref = ?");
                $Query->bind_param("s", $id);
                $Query->execute();
                $result = $Query->get_result();
        
               
                $row = $result->fetch_assoc();
                
        
                $Query->close();
        
                echo json_encode($row);
        
            } catch (Exception $e) {
                echo json_encode(['error' => 'Failed to retrieve transaction: ' . $e->getMessage()]);
            }
        
            break;

        case 'get_user_withdrawals':
            $email = sanitizeInput($_POST['email']);
           
            // Basic validation
            if (empty($email)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Email is required.'
                ]);
                exit;
            }
            $userId = getUserIdByEmail($email); 
            // Prepare the SQL query to fetch withdrawals
            $stmt = $mysqli->prepare("SELECT * FROM withdrawals WHERE user_id = ? ORDER BY add_date DESC LIMIT 20");
            $stmt->bind_param("i", $userId);
            $stmt->execute();
            $result = $stmt->get_result();
        
            // Fetch all rows
            $withdrawals = [];
            while ($row = $result->fetch_assoc()) {
                $withdrawals[] = $row;
            }
        
            if (!empty($withdrawals)) {
                echo json_encode([
                    'success' => true,
                    'withdrawals' => $withdrawals
                ]);
            } else {
                echo json_encode([
                    'success' => false,
                    'message' => 'No withdrawal history found.'
                ]);
            }
        
            // Close statement
            $stmt->close();
            break;
        

        case 'fund_withdrawal':
            $accountNumber = sanitizeInput($_POST['account_number']);
            $accountName = sanitizeInput($_POST['account_name']);
            $bank = sanitizeInput($_POST['bank']);
            $amount = sanitizeInput($_POST['amount']);
            $email = sanitizeInput($_POST['email']);
            $gross = sanitizeInput($_POST['gross']);
            $time = time();
        
            // Basic validation
            if (empty($accountNumber) || empty($accountName) || empty($bank) || empty($amount) || empty($email) || empty($gross)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'All fields are required.'
                ]);
                exit;
            }
        
            $amount = floatval($amount);
            if ($amount <= 0) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Invalid withdrawal amount.'
                ]);
                exit;
            }
        
            // Check user's balance
            $stmt = $mysqli->prepare("SELECT ngn_balance, tx_pin FROM users WHERE email = ?");
            $stmt->bind_param("s", $email);
            $stmt->execute();
            $result = $stmt->get_result();
            $user = $result->fetch_assoc();
        
            if (!$user) {
                echo json_encode([
                    'success' => false,
                    'message' => 'User not found.'
                ]);
                exit;
            }
        
            if ($gross > $user['ngn_balance']) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Insufficient funds.'
                ]);
                exit;
            }
        
            $newBalance = $user['ngn_balance'] - $gross;
            $updateStmt = $mysqli->prepare("UPDATE users SET ngn_balance = ? WHERE email = ?");
            $updateStmt->bind_param("ds", $newBalance, $email);
            $updateStmt->execute();
        
            if ($updateStmt->affected_rows > 0) {
                $userId = getUserIdByEmail($email); 
                $logStmt = $mysqli->prepare("INSERT INTO withdrawals (user_id, add_date, email, amount, account_number, account_name, bank, gross) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
                $logStmt->bind_param("issdsssd", $userId, $time, $email, $amount, $accountNumber, $accountName, $bank, $gross);
                $logStmt->execute();
        
                if ($logStmt->affected_rows > 0) {
                    sendMail($email, "Withdrawal Request Received", "Heythere, <br> We wanted to notify you that a fund withdrawal of ₦$gross was requested from your account and it is now being processed. <p>Please contact us immediately if you did not place any withdrawal requst.</p>");
                    echo json_encode([
                        'success' => true,
                        'message' => 'Withdrawal request submitted successfully.'
                    ]);
                } else {
                    echo json_encode([
                        'success' => false,
                        'message' => 'Failed to log withdrawal. Please try again.'
                    ]);
                }
            } else {
                echo json_encode([
                    'success' => false,
                    'message' => 'Failed to process withdrawal. Please try again.'
                ]);
            }
        
            $stmt->close();
            $updateStmt->close();
            break;
        

        case 'check_user':
            $recipient = $_POST['recipient'];
 $recipient_info = $mysqli->prepare("SELECT first_name, last_name FROM users WHERE email = ?");
 if (!$recipient_info) {
     echo json_encode(['error' => 'Failed to prepare recipient info query: ' . $mysqli->error]);
     exit;
 }
 $recipient_info->bind_param("s", $recipient);
 $recipient_info_success = $recipient_info->execute();
 if (!$recipient_info_success) {
     echo json_encode(['error' => 'Failed to execute recipient info query: ' . $recipient_info->error]);
     exit;
 }
 $recipient_info_result = $recipient_info->get_result();
 $recipient_data = $recipient_info_result->fetch_assoc();

 echo json_encode([
     'success' => true,
     'first_name' => $recipient_data['first_name'],
     'last_name' => $recipient_data['last_name']
 ]);
            break;

      case 'send_c_to_c':

    // 🔐 AUTH
    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    // 👤 AUTHENTICATED SENDER (SOURCE OF TRUTH)
    $senderInfo = getUserInfo($token);
    if (!$senderInfo) {
        exit(json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]));
    }

    if ((int)$senderInfo['is_banned'] === 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Account restricted.'
        ]));
    }

    if ((int)$senderInfo['id_verified'] !== 1) {
        exit(json_encode([
            'success' => false,
            'message' => 'Complete KYC to proceed.'
        ]));
    }

    $senderId    = $senderInfo['id'];
    $senderEmail = $senderInfo['email'];

    // 🔒 ACTION LOCK (ANTI DOUBLE-SUBMIT)
    if (!acquireActionLock($senderId, 'c2c_transfer')) {
        exit(json_encode([
            'success' => false,
            'message' => 'Too many requests. Please wait.'
        ]));
    }

    // 📥 INPUT (DO NOT ACCEPT SENDER FROM CLIENT)
    $recipient = sanitizeInput($_POST['recipient'] ?? '', 'email');
    $amount    = (float) sanitizeInput($_POST['amount'] ?? 0);
    $currency  = strtolower(sanitizeInput($_POST['currency'] ?? ''));
    $narration = sanitizeInput($_POST['narration'] ?? '');

    if (!$recipient || $amount <= 0 || !$currency || !$narration) {
        releaseActionLock($senderId, 'c2c_transfer');
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request data.'
        ]));
    }

    // 💱 CURRENCY WHITELIST (PREVENT SQL INJECTION)
    $allowedCurrencies = ['ngn', 'usd', 'eur'];
    if (!in_array($currency, $allowedCurrencies, true)) {
        releaseActionLock($senderId, 'c2c_transfer');
        exit(json_encode([
            'success' => false,
            'message' => 'Unsupported currency.'
        ]));
    }

    $currencyCode = strtolower($currency);
    $balanceColumn = $currency . '_balance';

    // 🎯 RECIPIENT
    $recipientInfo = getUserInfo($recipient);
    if (!$recipientInfo) {
        releaseActionLock($senderId, 'c2c_transfer');
        exit(json_encode([
            'success' => false,
            'message' => 'Recipient not found.'
        ]));
    }

    if ((int)$recipientInfo['id'] === (int)$senderId) {
        releaseActionLock($senderId, 'c2c_transfer');
        exit(json_encode([
            'success' => false,
            'message' => 'You cannot send money to yourself.'
        ]));
    }

    $time = time();
    $ref  = 'send_' . bin2hex(random_bytes(10));

    $mysqli->begin_transaction();

    try {
        // 🔒 LOCK SENDER ROW
        $stmt = $mysqli->prepare("
            SELECT {$balanceColumn}
            FROM users
            WHERE id = ?
            FOR UPDATE
        ");
        $stmt->bind_param("i", $senderId);
        $stmt->execute();
        $stmt->bind_result($senderBalance);
        $stmt->fetch();
        $stmt->close();

        if ($senderBalance < $amount) {
            throw new Exception("Insufficient balance.");
        }

        // 💸 DEBIT SENDER (ATOMIC)
        $stmt = $mysqli->prepare("
            UPDATE users
            SET {$balanceColumn} = {$balanceColumn} - ?
            WHERE id = ? AND {$balanceColumn} >= ?
        ");
        $stmt->bind_param("did", $amount, $senderId, $amount);
        if (!$stmt->execute() || $stmt->affected_rows === 0) {
            throw new Exception("Debit failed.");
        }
        $stmt->close();

        // 💰 CREDIT RECIPIENT
        $stmt = $mysqli->prepare("
            UPDATE users
            SET {$balanceColumn} = {$balanceColumn} + ?
            WHERE id = ?
        ");
        $stmt->bind_param("di", $amount, $recipientInfo['id']);
        if (!$stmt->execute()) {
            throw new Exception("Credit failed.");
        }
        $stmt->close();

        // 🧾 TRANSACTION LOGS
        $stmt = $mysqli->prepare("
            INSERT INTO transactions
            (flow, currency, type, title, description, date, status, our_ref, amount, email, user_id, sender, recipient)
            VALUES
            ('out', ?, 'debit', 'Internal Transfer', ?, ?, 'Completed', ?, ?, ?, ?, ?, ?)
        ");
        $desc = "You sent money to {$recipientInfo['first_name']} {$recipientInfo['last_name']}";
       $stmt->bind_param(
    "ssisdsiss",
    $currencyCode,   // s
    $desc,           // s
    $time,           // i
    $ref,            // s
    $amount,         // d
    $senderEmail,    // s
    $senderId,       // i
    $senderEmail,    // s
    $recipient       // s
);

        $stmt->execute();
        $stmt->close();

        $stmt = $mysqli->prepare("
            INSERT INTO transactions
            (flow, currency, type, title, description, date, status, our_ref, amount, email, user_id, sender, recipient)
            VALUES
            ('in', ?, 'credit', 'Internal Transfer', ?, ?, 'Completed', ?, ?, ?, ?, ?, ?)
        ");
        $desc2 = "You received money from {$senderInfo['first_name']} {$senderInfo['last_name']}";
      $stmt->bind_param(
    "ssisdsiss",
    $currencyCode,          // s
    $desc2,                 // s
    $time,                  // i
    $ref,                   // s
    $amount,                // d
    $recipient,             // s
    $recipientInfo['id'],   // i
    $senderEmail,           // s
    $recipient              // s
);

        $stmt->execute();
        $stmt->close();

        $mysqli->commit();
        releaseActionLock($senderId, 'c2c_transfer');

        // 📄 RECEIPT
        /*
        $receiptHtml = "
            <h3>Internal Transfer Successful</h3>
            <p><strong>Reference:</strong> {$ref}</p>
            <p><strong>From:</strong> {$senderInfo['first_name']} {$senderInfo['last_name']}</p>
            <p><strong>To:</strong> {$recipientInfo['first_name']} {$recipientInfo['last_name']}</p>
            <p><strong>Amount:</strong> {$currencyCode} " . number_format($amount, 2) . "</p>
            <p><strong>Narration:</strong> {$narration}</p>
            <p><strong>Date:</strong> " . date('Y-m-d H:i:s', $time) . "</p>
        ";
        */
       // $pdfUrl  = generateReceipt($ref, $receiptHtml);
      //  $fullUrl = $baseURL . $pdfUrl;

       $subject = "You have received a transfer";

$body = "
    <h3 style='color:#1584d4;'>Incoming Wallet Transfer</h3>

    <p>Hello <strong>{$recipientInfo['first_name']}</strong>,</p>

    <p>You have successfully received a wallet transfer. Below are the details:</p>

    <table border='1' cellpadding='10' cellspacing='2' width='100%' style='border-collapse:collapse;'>
        <tr>
            <th align='left' width='40%'>Transaction Reference</th>
            <td>{$ref}</td>
        </tr>
        <tr>
            <th align='left'>Sender</th>
            <td>{$senderInfo['first_name']} {$senderInfo['last_name']}</td>
        </tr>
        <tr>
            <th align='left'>Amount Received</th>
            <td>{$currencyCode} " . number_format($amount, 2) . "</td>
        </tr>
        <tr>
            <th align='left'>Narration</th>
            <td>{$narration}</td>
        </tr>
        <tr>
            <th align='left'>Status</th>
            <td style='color:green;'>Completed</td>
        </tr>
        <tr>
            <th align='left'>Date</th>
            <td>" . date('Y-m-d H:i:s', $time) . "</td>
        </tr>
      
    </table>

    <p style='margin-top:15px;'>
        The funds have been credited to your wallet and are available for use immediately.
    </p>

    <p>
        If you do not recognize this transaction, please contact support immediately.
    </p>

    <p>
        Thank you for using <strong>Vinpace</strong>.
    </p>
";

sendMail($recipient, $subject, $body);

     
        addNotification(
            $recipientInfo['id'],
            "You received {$currencyCode} " . number_format($amount, 2) . " from {$senderInfo['first_name']}."

        );

        echo json_encode([
            'success' => true,
            'message' => 'Transfer successful.',
            'ref'     => $ref,
            'amount'  => $amount,
            'currency'=> $currencyCode,
          //  'receipt' => $fullUrl
        ]);

    } catch (Exception $e) {
        $mysqli->rollback();
        releaseActionLock($senderId, 'c2c_transfer');

        echo json_encode([
            'success' => false,
            'message' => 'Transaction failed: ' . $e->getMessage()
        ]);
    }

    break;

        

       case 'change_pin':
          $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    $user = getUserInfo($token);

            $oldPin = $_POST['oldPin'] ?? '';
            $newPin = $_POST['newPin'] ?? '';

            // Basic validation for required fields
            if (empty($oldPin) || empty($newPin)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Please enter all required fields.'
                ]);
                exit;
            }

            // Validate new PIN length
            if (strlen($newPin) !== 4 || !is_numeric($newPin)) {
                echo json_encode([
                    'success' => false,
                    'message' => 'New PIN must be exactly 4 digits.'
                ]);
                exit;
            }

            // Check if user exists and old PIN matches
            if (!$user || (int)$user['tx_pin'] !== (int)$oldPin) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Old PIN is incorrect...'
                ]);
                exit;
            }

            // Prepare SQL to update the PIN
            $updateStmt = $mysqli->prepare("UPDATE users SET tx_pin = ? WHERE email = ?");
            $updateStmt->bind_param("ss", $newPin, $user['email']);
            $updateStmt->execute();

            // Check if the update was successful
            if ($updateStmt->affected_rows > 0) {
                sendMail($user['email'], "Your PIN was changed", "Hey there, <br> We wanted to notify you that your Transaction PIN has been changed successfully. <p>Please contact us now if you did not change your PIN.</p>");
                addNotification($user['id'], "Your transaction PIN has been  changed.  Report it immediately if not done by you.", 'Security Alert!');

                echo json_encode([
                    'success' => true,
                    'message' => 'Transaction PIN has been changed successfully.'
                ]);
            } else {
                echo json_encode([
                    'success' => false,
                    'message' => 'Failed to change PIN. Please try again.'
                ]);
            }

         
            $updateStmt->close();
            break;

              case "delete_my_account":
  $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    $u = getUserInfo($token);
$user_id = $u['id'];
    if (empty($u)) {
        echo json_encode([
            'success' => false,
            'message' => 'User not found.'
        ]);
        exit;
    }

    // Use prepared statements to prevent SQL injection
    try {
       $mysqli->begin_transaction();

        // Delete from dependent tables first (respect foreign key constraints)
        $tables = ['notifications', 'transactions'];
        foreach ($tables as $table) {
            $stmt = $mysqli->prepare("DELETE FROM `$table` WHERE user_id = ?");
            $stmt->execute([$user_id]);
        }

        // Finally delete the user
        $stmt = $mysqli->prepare("DELETE FROM `users` WHERE id = ?");
        $stmt->execute([$user_id]);

         $stmt2 = $mysqli->prepare("DELETE FROM `trades` WHERE email = ?");
        $stmt2->execute([$u['email']]);

        $mysqli->commit();

        // Send email notification
        $to = $u['email'];
        $subject = "Account Deleted Successfully";
        $html = "
        <html>
        <body style='font-family: Arial, sans-serif;'>
            <h2>Goodbye, {$u['first_name']}!</h2>
            <p>Your account on <b>{$set['app_name']}</b> has been permanently deleted as requested.</p>
            <p>If you didn't initiate this deletion, please contact our support team immediately.</p>
            <br>
            <p>Best regards,<br><b>{$set['app_name']} Team</b></p>
        </body>
        </html>
        ";

        if (sendMail($to, $subject, $html)) {
            echo json_encode([
                'success' => true,
                'message' => 'Your account has been permanently deleted. A confirmation email has been sent.'
            ]);
        } else {
            echo json_encode([
                'success' => true,
                'message' => 'Your account has been deleted, but the confirmation email could not be sent.'
            ]);
        }

    } catch (Exception $e) {
        $mysqli->rollBack();
        echo json_encode([
            'success' => false,
            'message' => 'An error occurred while deleting your account: ' . $e->getMessage()
        ]);
    }
    break;


        case 'update_bank_settings':
          $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

  $u = getUserInfo($token);
    if (!$u || (int)$u['id_verified'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
        exit;
    }
            $bankName = sanitizeInput($_POST['withdrawal_bank']);
            $accountNumber = sanitizeInput($_POST['withdrawal_bank_account']);
            $accountName = strtoupper($u['first_name'])." ".strtoupper($u['last_name']); 
            $fName = sanitizeInput($_POST['fName']);
             $lName = sanitizeInput($_POST['lName']);

            $code = sanitizeInput($_POST['withdrawal_bank_code']);
        
            // Basic validation
            if (empty($lName) || empty($fName) || empty($bankName) || empty($accountNumber) || empty($accountName) || empty($code)) {
                echo json_encode(['success' => false, 'message' => 'Please fill in all bank details.']);
                exit;
            }
        
        if (
    strtolower(trim($fName)) !== strtolower($u['first_name']) ||
    strtolower(trim($lName)) !== strtolower($u['last_name'])
) {
    echo json_encode([
        'success' => false,
        'message' => 'First name or last name do not match the names on your BVN.'
    ]);
    exit;
}
 
            // Step 1: Verify that the user exists based on the email
            $userId = $u['id'];
           
        
            // Step 2: Update the user's bank information in the database
            $updateBankQuery = $mysqli->prepare("
                UPDATE users 
                SET withdrawal_bank = ?, withdrawal_bank_account = ?, withdrawal_bank_name = ?, withdrawal_bank_code = ? 
                WHERE id = ?
            ");
            $updateBankQuery->bind_param("ssssi", $bankName, $accountNumber, $accountName, $code, $userId);
        
            if ($updateBankQuery->execute()) {
                sendMail($email, "Your withdrawal bank account was changed", "Hey there, <br> We wanted to notify you that your linked bank account has been changed successfully. <p>Please contact us now if you did not change your withdrawal bank account.</p>");
                echo json_encode(['success' => true, 'message' => 'Withdrawal bank details have been updated successfully.']);
            } else {
                echo json_encode(['success' => false, 'message' => 'Failed to update bank details.']);
            }
        
            $updateBankQuery->close();
            break;
        

   case 'update_password':
      $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    $u = getUserInfo($token);
     if (!$u) {
                echo json_encode(['success' => false, 'message' => 'Invalid request.']);
                exit;
            }
            // Get the input data
            $newPassword = sanitizeInput($_POST['newPassword']);
        
            // Ensure the data is valid
            if (empty($newPassword)) {
                echo json_encode(['success' => false, 'message' => 'Email and new password are required.']);
                exit;
            }
            if(strlen($newPassword) < 8){
                echo json_encode(['success' => false, 'message' => 'Password must have a minimum of 8 characters.']);
                exit;
            }
    
        $userId = $u['id'];
        
            // Step 2: Hash the new password
            $hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
        
            // Step 3: Update the password in the database
            $updateQuery = $mysqli->prepare("UPDATE users SET password = ? WHERE id = ?");
            $updateQuery->bind_param("si", $hashedPassword, $userId);
        $name = $u['first_name'];
            if ($updateQuery->execute()) {
                sendMail($email, "Your password was changed", "Hey $name, <br> We wanted to notify you that your password has been changed successfully. <p>Please contact us now if you did not change your password.</p>");
                echo json_encode(['success' => true, 'message' => 'Password has been changed successfully.']);
                addNotification($u['id'], "Your password has been  changed. Report it immediately if not done by you.", 'Security Alert!');

            } else {
                echo json_encode(['success' => false, 'message' => 'Failed to update the password.']);
            }
        
            $updateQuery->close();
            break;
        
        

        case 'change_photo':
        $email = sanitizeInput($_POST['email']);
        $userId = getUserIdByEmail($email);
            if (!$userId) {
                echo json_encode(['success' => false, 'message' => 'User is not authenticated.']);
                exit;
            }
        
            // Check if a file was uploaded
            if (!isset($_FILES['profilePhoto']) || $_FILES['profilePhoto']['error'] !== UPLOAD_ERR_OK) {
                echo json_encode(['success' => false, 'message' => 'No photo selected or upload error.']);
                exit;
            }
        
            // Set directory and validate the file
            $uploadDir = 'uploads/users/';
            $fileTmpPath = $_FILES['profilePhoto']['tmp_name'];
            $fileName = basename($_FILES['profilePhoto']['name']);
            $fileSize = $_FILES['profilePhoto']['size'];
            $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
            $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
        
            if (!in_array($fileExt, $allowedExtensions)) {
                echo json_encode(['success' => false, 'message' => 'Invalid file format. Allowed: jpg, jpeg, png, gif.']);
                exit;
            }
        
            if ($fileSize > 5 * 1024 * 1024) { // Max size: 2MB
                echo json_encode(['success' => false, 'message' => 'File size exceeds the maximum limit of 5MB.']);
                exit;
            }
        
           
            if (!file_exists($uploadDir)) {
                mkdir($uploadDir, 0777, true);
            }
        
           
            $newFileName = $userId . '_' . time() . '.' . $fileExt;
            $destPath = $uploadDir . $newFileName;
        
            // Attempt to move the uploaded file
            if (move_uploaded_file($fileTmpPath, $destPath)) {
                // Update the profile photo path in the database
                $updateQuery = $mysqli->prepare("UPDATE users SET photo = ? WHERE id = ?");
                $updateQuery->bind_param("si", $destPath, $userId);
                if ($updateQuery->execute()) {
                    echo json_encode(['success' => true, 'message' => 'Profile photo updated successfully.']);
                } else {
                    echo json_encode(['success' => false, 'message' => 'Failed to update profile photo in the database.']);
                }
                $updateQuery->close();
            } else {
                echo json_encode(['success' => false, 'message' => 'Failed to upload the photo.']);
            }
        
            break;
        

        case 'update_read_notifications':
            // Sanitize and retrieve the user's email from POST data
            $email = sanitizeInput($_POST['email']);
        
            try {
                // Step 1: Get user ID based on email
                $userId = getUserIdByEmail($email);
                if ($userId === null) {
                    echo json_encode(['error' => 'User not found']);
                    exit;
                }
        
                // Step 2: Update unread notifications to mark them as read
                $updateQuery = $mysqli->prepare("UPDATE notifications SET is_read = 1 WHERE user_id = ? AND is_read = 0");
                $updateQuery->bind_param("i", $userId);
                $updateQuery->execute();
        
                // Check if any rows were affected
                if ($updateQuery->affected_rows > 0) {
                    echo json_encode(['success' => true, 'message' => 'Notifications marked as read.']);
                } else {
                    echo json_encode(['success' => true, 'message' => 'No unread notifications to update.']);
                }
        
                $updateQuery->close();
        
            } catch (Exception $e) {
                echo json_encode(['error' => 'Failed to update notifications: ' . $e->getMessage()]);
            }
        
            break;

        case 'get_unread_notifications':
            // Sanitize and retrieve the user's email from POST data
            $email = sanitizeInput($_POST['email']);
            
            // Initialize unread count to zero
            $unreadCount = 0;
        
            try {
                // Step 1: Get user ID based on email
                $userId = getUserIdByEmail($email);
                if ($userId === null) {
                    echo json_encode(['error' => 'User not found']);
                    exit;
                }
        
                // Step 2: Count unread notifications for the user
                $unreadQuery = $mysqli->prepare("SELECT COUNT(*) FROM notifications WHERE user_id = ? AND is_read = 0");
                $unreadQuery->bind_param("i", $userId);
                $unreadQuery->execute();
                $unreadQuery->bind_result($unreadCount);
                $unreadQuery->fetch();
                $unreadQuery->close();
        
                // Return the unread notifications count
                echo json_encode(['success' => true, 'unread' => $unreadCount]);
        
            } catch (Exception $e) {
                echo json_encode(['error' => 'Failed to retrieve unread notifications: ' . $e->getMessage()]);
            }
        
            break;

        case 'get_user_notifications':
            // Sanitize and retrieve user email from POST data
            $email = sanitizeInput($_POST['email']);
            
            // Start with an empty array for the result
            $notifications = [];
        
            try {
                // Step 1: Get user ID based on email
                $userId = getUserIdByEmail($email);
                if ($userId === null) {
                    echo json_encode(['error' => 'User not found']);
                    exit;
                }
        
                // Step 2: Query notifications for the user
                $notificationsQuery = $mysqli->prepare("SELECT * FROM notifications WHERE user_id = ? ORDER BY add_date DESC LIMIT 15");
                $notificationsQuery->bind_param("i", $userId);
                $notificationsQuery->execute();
                $result = $notificationsQuery->get_result();
        
                // Fetch all notifications and store them in the array
                while ($row = $result->fetch_assoc()) {
                    $notifications[] = $row;
                }
        
                $notificationsQuery->close();
        
                // Return notifications in JSON format
                echo json_encode(['success' => true, 'notifications' => $notifications]);
        
            } catch (Exception $e) {
                echo json_encode(['error' => 'Failed to retrieve notifications: ' . $e->getMessage()]);
            }
        
            break;

        case 'get_user_summary':
            $email = sanitizeInput($_POST['email']);
            
            // Begin with an array to store the result
            $result = [
                'total_transactions' => 0,
                'total_spent' => 0.0,
                'referrals' => [], // Initialize the referrals as an empty array
            ];
        
            // Start a transaction for consistent read
            $mysqli->begin_transaction();
        
            try {
                // Step 1: Get user ID based on email
                $userId = getUserIdByEmail($email);
                if ($userId === null) {
                    echo json_encode(['error' => 'User not found']);
                    exit;
                }
        
                // Step 2: Get the total transaction count for the user
                $transCountQuery = $mysqli->prepare("SELECT COUNT(*) FROM transactions WHERE email = ? OR user_id = ?");
                $transCountQuery->bind_param("si", $email, $userId);
                $transCountQuery->execute();
                $transCountQuery->bind_result($result['total_transactions']);
                $transCountQuery->fetch();
                $transCountQuery->close();
        
                // Step 3: Get the total amount spent by the user
                $spentQuery = $mysqli->prepare("SELECT total_spent FROM users WHERE id = ?");
                $spentQuery->bind_param("i", $userId);
                $spentQuery->execute();
                $spentQuery->bind_result($result['total_spent']);
                $spentQuery->fetch();
                $spentQuery->close();
        
                // Step 4: Get the list of referrals by the user
                $referralsQuery = $mysqli->prepare("SELECT id, first_name, email, total_spent, ref_paid FROM users WHERE ref_by = ?");
                $referralsQuery->bind_param("i", $userId);
                $referralsQuery->execute();
                $referralsResult = $referralsQuery->get_result();
        
                // Loop through each referral and store in the array
                while ($row = $referralsResult->fetch_assoc()) {
                    $result['referrals'][] = $row;
                }
        
                $referralsQuery->close();
        
                // Commit transaction and return the result
                $mysqli->commit();
                echo json_encode($result);
        
            } catch (Exception $e) {
                // Rollback transaction in case of an error
                $mysqli->rollback();
                echo json_encode(['error' => 'Failed to retrieve user summary: ' . $e->getMessage()]);
            }
        
            break;
        
        

        case 'record_bank_account':

    $ref            = sanitizeInput($_POST['ref']);
    $account_number = sanitizeInput($_POST['account_number']);
    $account_name   = sanitizeInput($_POST['account_name']);
    $bank_name      = sanitizeInput($_POST['bank_name']);
    $bank_code      = sanitizeInput($_POST['bank_code']);
    $email          = sanitizeInput($_POST['email'], 'email');

    if (!$ref || !$account_number || !$account_name || !$bank_name || !$bank_code || !$email) {
        exit(json_encode([
            'status'  => 'error',
            'message' => 'Invalid request data.'
        ]));
    }

    // 🔎 Fetch user (for notification & safety)
    $user = getUserInfo($email);
    if (!$user) {
        exit(json_encode([
            'status'  => 'error',
            'message' => 'User not found.'
        ]));
    }

    // ✅ Insert bank account
    $stmt = $mysqli->prepare("
        INSERT INTO bank_accounts 
        (ref, account_number, account_name, bank_name, bank_code, email, user_id) 
        VALUES (?, ?, ?, ?, ?, ?, ?)
    ");
    $stmt->bind_param(
        "sissssi",
        $ref,
        $account_number,
        $account_name,
        $bank_name,
        $bank_code,
        $email,
        $user['id']
    );

    if ($stmt->execute()) {

        // 📧 EMAIL USER
        $subject = "Bank Account Added Successfully";
        $body = "
            <p>Hello {$user['first_name']},</p>
            <p>A new bank account has just been added to your profile.</p>
            <ul>
                <li><strong>Bank:</strong> {$bank_name}</li>
                <li><strong>Account Name:</strong> {$account_name}</li>
                <li><strong>Account Number:</strong> ****" . substr($account_number, -4) . "</li>
                <li><strong>Reference:</strong> {$ref}</li>
            </ul>
            <p>If you did not perform this action, please contact support immediately.</p>
            <p>— Swapeki Security Team</p>
        ";
        sendMail($email, $subject, $body);

        // 🔔 PUSH / IN-APP NOTIFICATION
        addNotification(
            $user['id'],
            "A new bank account ({$bank_name} • ****" . substr($account_number, -4) . ") was added to your profile.",
            "Security Alert!"
        );

        echo json_encode([
            'status'  => 'success',
            'message' => 'Bank account added successfully.'
        ]);

    } else {

        error_log("Bank account insert failed: " . $stmt->error);

        echo json_encode([
            'status'  => 'error',
            'message' => 'Failed to save bank account.'
        ]);
    }

    $stmt->close();
    break;


     case 'get_user_vaccount':
    $email = sanitizeInput($_POST['email'], 'email');
    if (!$email) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid email address!'
        ]);
        exit;
    }

    // Fetch all accounts for the user
    $stmt = $mysqli->prepare("SELECT * FROM bank_accounts WHERE email = ?");
    if (!$stmt) {
        echo json_encode([
            'success' => false,
            'message' => 'DB error!'
        ]);
        exit;
    }

    $stmt->bind_param("s", $email);
    $stmt->execute();

    $result = $stmt->get_result();
    $accounts = [
        'ngn' => null,
        'usd' => null,
        'eur' => null,
        'gbp' => null
    ];

    while ($row = $result->fetch_assoc()) {
        $currency = strtolower($row['currency'] ?? '');
        if (in_array($currency, ['ngn', 'usd', 'eur', 'gbp'])) {
            $accounts[$currency] = $row;
        }
    }

    $stmt->close();

    echo json_encode([
        'success' => true,
        'data' => $accounts
    ]);

    break;


     case 'validate_bvn':

    if (!isset($_POST['bvn'])) {
        echo json_encode([
            'success' => false,
            'message' => 'BVN is required.'
        ]);
        break;
    }

     // 🚫 BLOCK BVN REUSE
    $check = $mysqli->prepare("
        SELECT id FROM users WHERE bvn = ? LIMIT 1
    ");
    $check->bind_param("s", $_POST['bvn']);
    $check->execute();
    $check->store_result();

    if ($check->num_rows > 0) {
        echo json_encode([
            'success' => false,
            'message' => 'This BVN has already been used on another account.'
        ]);
        exit;
    }
    $check->close();

    $result = validateBVN($_POST['bvn']);

    echo json_encode($result);
    break;

    case 'submit_kyc':
         $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        exit(json_encode([
            'success' => false,
            'message' => 'Invalid request session.'
        ]));
    }

    $country      = 'Nigeria';
    $state        = sanitizeInput($_POST['state'] ?? '');
    $city         = sanitizeInput($_POST['city'] ?? '');
    $address      = sanitizeInput($_POST['address'] ?? '');
    $postalCode   = sanitizeInput($_POST['postal_code'] ?? '');
    $bvn          = sanitizeInput($_POST['bvn'] ?? '');
  
    if (
         !$state || !$city ||
        !$address || !$postalCode || !$bvn
    ) {
        echo json_encode(['success' => false, 'message' => 'All required fields must be filled.']);
        exit;
    }

    // 👤 USER CHECK
    $user = getUserInfo($token);
    if (!$user) {
        echo json_encode(['success' => false, 'message' => 'User not found.']);
        exit;
    }

    if ((int)$user['email_verified'] !== 1) {
        echo json_encode(['success' => false, 'message' => 'Verify email first.']);
        exit;
    }

    if ((int)$user['id_verified'] === 1) {
        echo json_encode(['success' => false, 'message' => 'KYC already completed.']);
        exit;
    }
$email = $user["email"];
    // 🔍 RE-VALIDATE BVN (BACKEND SOURCE OF TRUTH)
    $bvnCheck = validateBVN($bvn);
    if (!$bvnCheck['success']) {
        echo json_encode(['success' => false, 'message' => 'BVN verification failed.']);
        exit;
    }

    $entity = $bvnCheck['data'];

    // ✅ FORCE VERIFIED DATA
    $first_name = $entity['first_name'];
    $last_name  = $entity['last_name'];
    $dob        = $entity['date_of_birth'];
   $phone = !empty($entity['phone_number1'])
    ? $entity['phone_number1']
    : (!empty($entity['phone_number']) ? $entity['phone_number'] : null);

    $bvnImage   = $entity['image'] ?? '';

    // 🚫 BLOCK BVN REUSE
    $check = $mysqli->prepare("
        SELECT id FROM users WHERE bvn = ? AND email != ? LIMIT 1
    ");
    $check->bind_param("ss", $bvn, $email);
    $check->execute();
    $check->store_result();

    if ($check->num_rows > 0) {
        echo json_encode([
            'success' => false,
            'message' => 'This BVN has already been used on another account.'
        ]);
        exit;
    }
    $check->close();

  
    // 🧾 UPDATE USER (SERVER-CONTROLLED)
    $stmt = $mysqli->prepare("
        UPDATE users SET
        bvn_phone = ?,
            first_name = ?,
            last_name  = ?,
            dob        = ?,
            country    = ?,
            state      = ?,
            city       = ?,
            address    = ?,
            postal_code= ?,
            bvn        = ?,
            id_verified = 1
        WHERE email = ?
    ");

    $stmt->bind_param(
        "sssssssssss",
        $phone,
        $first_name,
        $last_name,
        $dob,
        $country,
        $state,
        $city,
        $address,
        $postalCode,
        $bvn,
        $email
    );

    if (!$stmt->execute()) {
        error_log("KYC UPDATE ERROR: " . $stmt->error);
        echo json_encode(['success' => false, 'message' => 'KYC submission failed.']);
        exit;
    }

    $stmt->close();
//UPDATE SUBSCRIBE
                       
/*
$json = [
    "firstname" => $first_name,
    "lastname" => $last_name,
    "email" => $email,
    "groups" => ["e13wpq"]
];
  
  $response = $client->get(
      "https://api.sender.net/v2/subscribers/$email",
      [
          'headers' => [
              'Authorization' => 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiMTRlZDFkYjY2Zjg1NmIwOWFlMzYxOGNkYzVjNTUwOGQwODlkNTE3MGM5OWJlYTg5YmE1NmE3OTUwMGZkNjc4ZmU1NmE3YTIzNTAyMDViNWQiLCJpYXQiOiIxNzQ2OTIxMjAxLjQyMzI0NyIsIm5iZiI6IjE3NDY5MjEyMDEuNDIzMjQ5IiwiZXhwIjoiNDkwMDUyMTIwMS40MjIwNzAiLCJzdWIiOiI5NzY2OTYiLCJzY29wZXMiOltdfQ.MZEO6oE_khxhJgbF8Wnjt2tpKOPfOcyHSGxDHXpXKrLtvk6j6HTr8qMCiYe0ECDwYf5D9Rkj7fBQKDd1Vg7PAw',
              'Content-Type' => 'application/json',
              'Accept' => 'application/json',
          ],
          'json' => $json
      ]
  );
  $body = $response->getBody()->getContents();
  $data = json_decode($body, true);
  */
    echo json_encode([
        'success' => true,
        'message' => 'KYC verified successfully.'
    ]);
    break;




case 'get_onboarding':
    $Query = "SELECT * FROM onboarding ORDER BY id DESC LIMIT 4";
    $result = $mysqli->query($Query);
    
    if ($result->num_rows > 0) {
        $records = [];
        while ($row = $result->fetch_assoc()) {
            $records[] = $row;
        }
        // Sort the records in ascending order by id
        usort($records, function ($a, $b) {
            return $a['id'] - $b['id'];
        });
        echo json_encode($records);
    } else {
        echo json_encode(['error' => 'Failed to fetch slides']);
    }
    
    break;

    case 'delete_ad':
        try {
          $id = (int)$_POST['id'];
          $res = $mysqli->query("SELECT image FROM ads WHERE id = $id LIMIT 1");
          if ($res && $res->num_rows > 0) {
            $row = $res->fetch_assoc();
            if (file_exists($row['image'])) unlink($row['image']);
          }
      
          $mysqli->query("DELETE FROM ads WHERE id = $id");
          echo json_encode(['success' => true, 'message' => 'Ad deleted.']);
        } catch (Throwable $e) {
          echo json_encode(['success' => false, 'message' => 'Error deleting ad: ' . $e->getMessage()]);
        }
        break;
      

    case 'get_ads':
        $Query = "SELECT * FROM ads ORDER BY id DESC";
        $result = $mysqli->query($Query);
        
        if ($result->num_rows > 0) {
            $records = [];
            while ($row = $result->fetch_assoc()) {
                $records[] = $row;
            }
         
            echo json_encode($records);
        } else {
            echo json_encode(['error' => 'No ads found!']);
        }
        
        break;

     case 'get_crypto_transactions':

    $email = sanitizeInput($_POST['email'] ?? '', 'email');
    $page  = intval($_POST['page'] ?? 1);
    $limit = intval($_POST['limit'] ?? 20);

    if (!$email) {
        echo json_encode(['success' => false, 'message' => 'Invalid user']);
        exit;
    }

    if ($page < 1) $page = 1;
    if ($limit < 1) $limit = 20;

    $offset = ($page - 1) * $limit;

    // Count total transactions
    $countStmt = $mysqli->prepare("SELECT COUNT(*) AS total FROM transactions WHERE email = ? AND type = 'crypto'");
    $countStmt->bind_param("s", $email);
    $countStmt->execute();
    $countResult = $countStmt->get_result()->fetch_assoc();
    $total = intval($countResult['total']);
    $totalPages = ceil($total / $limit);

    // Fetch paginated records
    $stmt = $mysqli->prepare("
        SELECT * FROM transactions 
        WHERE email = ? AND type = 'crypto' 
        ORDER BY id DESC 
        LIMIT ? OFFSET ?
    ");
    $stmt->bind_param("sii", $email, $limit, $offset);
    $stmt->execute();
    $result = $stmt->get_result();

    $records = [];
    while ($row = $result->fetch_assoc()) {
        $records[] = $row;
    }

    echo json_encode([
        'success' => true,
        'transactions' => $records,
        'page' => $page,
        'limit' => $limit,
        'total' => $total,
        'total_pages' => $totalPages
    ]);

    break;
      

          case 'get_paged_transactions':

    $email = sanitizeInput($_POST['email'] ?? '', 'email');
    $page  = intval($_POST['page'] ?? 1);
    $limit = intval($_POST['limit'] ?? 20);

    if (!$email) {
        echo json_encode(['success' => false, 'message' => 'Invalid user']);
        exit;
    }

    if ($page < 1) $page = 1;
    if ($limit < 1) $limit = 20;

    $offset = ($page - 1) * $limit;

    // Count total transactions
    $countStmt = $mysqli->prepare("SELECT COUNT(*) AS total FROM transactions WHERE email = ?");
    $countStmt->bind_param("s", $email);
    $countStmt->execute();
    $countResult = $countStmt->get_result()->fetch_assoc();
    $total = intval($countResult['total']);
    $totalPages = ceil($total / $limit);

    // Fetch paginated records
    $stmt = $mysqli->prepare("
        SELECT * FROM transactions 
        WHERE email = ? 
        ORDER BY id DESC 
        LIMIT ? OFFSET ?
    ");
    $stmt->bind_param("sii", $email, $limit, $offset);
    $stmt->execute();
    $result = $stmt->get_result();

    $records = [];
    while ($row = $result->fetch_assoc()) {
        $records[] = $row;
    }

    echo json_encode([
        'success' => true,
        'transactions' => $records,
        'page' => $page,
        'limit' => $limit,
        'total' => $total,
        'total_pages' => $totalPages
    ]);

    break;

        case 'get_transactions':

    if (!isset($_POST['email'])) {
        echo json_encode(['error' => 'Email is required']);
        break;
    }

    $email  = trim($_POST['email']);
    $u = getUserInfo($email);
    $userId = $u['id'] ?? null;

    // Prepare SQL
    $stmt = $mysqli->prepare(
        "SELECT * 
         FROM transactions 
         WHERE email = ? OR user_id = ?
         ORDER BY id DESC"
    );

    if (!$stmt) {
        echo json_encode(['error' => 'Failed to prepare query']);
        break;
    }

    // Bind parameters
    $stmt->bind_param("si", $email, $userId);

    // Execute
    $stmt->execute();

    // Get result
    $result = $stmt->get_result();

    if ($result && $result->num_rows > 0) {
        $records = [];

        while ($row = $result->fetch_assoc()) {
            $records[] = $row;
        }

        echo json_encode($records);
    } else {
        echo json_encode(['error' => 'No transactions found!']);
    }

    $stmt->close();
    break;


           case 'get_all_transactions':
    $limit = intval($_POST['limit'] ?? 100);
    $stmt = $mysqli->prepare("SELECT * FROM transactions ORDER BY id DESC LIMIT ?");
    $stmt->bind_param("i", $limit);
    $stmt->execute();
    $res = $stmt->get_result();
    $txs = $res->fetch_all(MYSQLI_ASSOC);
    echo json_encode(['success' => true, 'transactions' => $txs]);
    break;

// ✅ Search transactions by email or reference
case 'search_transactions':
    $query = sanitizeInput($_POST['query']);
    $like = "%$query%";
    $stmt = $mysqli->prepare("SELECT * FROM transactions WHERE email LIKE ? OR our_ref LIKE ? ORDER BY id DESC");
    $stmt->bind_param("ss", $like, $like);
    $stmt->execute();
    $res = $stmt->get_result();
    $txs = $res->fetch_all(MYSQLI_ASSOC);
    echo json_encode(['success' => true, 'transactions' => $txs]);
    break;

      case 'get_user_pin':
    $user_id = (int)$_POST['user_id'];
    $password = sanitizeInput($_POST['password']);
 $appName = $set['app_name'];
    if (empty($user_id) || empty($password)) {
        echo json_encode(['success' => false, 'message' => 'Missing credentials.']);
        exit;
    }

    // Fetch user credentials and email
    $stmt = $mysqli->prepare("SELECT email, first_name, password, tx_pin FROM users WHERE id = ? LIMIT 1");
    $stmt->bind_param("i", $user_id);
    $stmt->execute();
    $result = $stmt->get_result();
    $user = $result->fetch_assoc();
    $stmt->close();

    if (!$user) {
        echo json_encode(['success' => false, 'message' => 'User not found.']);
        exit;
    }

    if (!password_verify($password, $user['password'])) {
        echo json_encode(['success' => false, 'message' => 'Incorrect password.']);
        exit;
    }

    if (empty($user['tx_pin'])) {
        echo json_encode(['success' => false, 'message' => 'You have not set a transaction PIN.']);
        exit;
    }

    // Prepare and send email
    $subject = "Your Transaction PIN";
    $body = "
        <p>Dear " . htmlspecialchars($user['first_name']) . ",</p>
        <p>Your transaction PIN is:</p>
        <h2 style='color:#2c3e50; font-size:22px;'>" . htmlspecialchars($user['tx_pin']) . "</h2>
        <p>Please keep it safe and do not share it with anyone.</p>
        <br>
        <p>Best regards,<br><strong>$appName Security Team</strong></p>
    ";

    if (sendMail($user['email'], $subject, $body)) {
        echo json_encode(['success' => true, 'message' => 'PIN has been sent to your email.']);
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to send PIN email.']);
    }
    break;



  case 'user_login':

    if (PHP_SAPI === 'cli') {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid login request.'
        ]);
        exit;
    }

    $identifier = sanitizeInput($_POST['identifier'] ?? '');
    $pass       = $_POST['pass'] ?? '';

    if (!$identifier || !$pass) {
        echo json_encode([
            'success' => false,
            'message' => 'Please fill in all fields.'
        ]);
        exit;
    }

    $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
    $fingerprint = hash('sha256', $ip . $ua);

    if (!acquireFingerprintLock($fingerprint, 'login', 20)) {
        echo json_encode([
            'success' => false,
            'message' => 'Too many attempts. Please wait 20 seconds.'
        ]);
        exit;
    }

    try {
        $stmt = $mysqli->prepare("
            SELECT id, first_name, email, password, is_banned
            FROM users
            WHERE email = ? OR phone = ?
            LIMIT 1
        ");
        $stmt->bind_param("ss", $identifier, $identifier);
        $stmt->execute();
        $res = $stmt->get_result();

        usleep(300000); // timing attack protection

        if ($res->num_rows === 0) {
            throw new Exception('Invalid login credentials.');
        }

        $user = $res->fetch_assoc();
        $stmt->close();

        if ((int)$user['is_banned'] === 1) {
            throw new Exception('Account restricted. Contact support.');
        }

        if (!password_verify($pass, $user['password'])) {
            throw new Exception('Invalid login credentials.');
        }

        // 🔐 Generate 6-digit code
        $code      = random_int(100000, 999999);
        $expiresAt = time() + 300; // 5 minutes

        $upd = $mysqli->prepare("
            UPDATE users
            SET verify_token = ?, verify_expires = ?
            WHERE id = ?
        ");
        $upd->bind_param("iii", $code, $expiresAt, $user['id']);
        $upd->execute();
        $upd->close();
  $app = $set['app_name'];
  $name = $user['first_name'];
        $subject = "Your verification code";

        $html = "
            <div style='font-family:Arial,sans-serif;max-width:600px;margin:auto;padding:20px;background:#f9f9f9;border-radius:8px;'>
                <h2 style='color:#1584d4;'>Complete your request!</h2>
                <p>Hello {$name}, Your verification code is:</p>
                <h1 style='color:#333;background:#e3f2fd;padding:10px;border-radius:6px;text-align:center;'>
                    {$code}
                </h1>
                <p>Please enter this code in the app to complete your request.</p>
                <hr>
                <p style='font-size:13px;color:#666;'>© " . date('Y') . " {$app}. All rights reserved.</p>
            </div>
        ";

        sendMail($user['email'], $subject, $html);

        releaseActionLock($fingerprint, 'login');

        echo json_encode([
            'success' => true,
            'message' => 'Enter the verification code sent to your email.',
            'email'   => $user['email']
        ]);
        exit;

    } catch (Exception $e) {
        echo json_encode([
            'success' => false,
            'message' => $e->getMessage()
        ]);
        exit;
    }

break;

case 'user_login_2':

    $identifier = sanitizeInput($_POST['identifier'] ?? '');
    $code       = sanitizeInput($_POST['code'] ?? '', 'string');

    if (!$identifier || !$code) {
        echo json_encode([
            'success' => false,
            'message' => 'Please fill in all fields.'
        ]);
        exit;
    }

    $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

    $stmt = $mysqli->prepare("
        SELECT id, first_name,  email, verify_token, verify_expires, ip
        FROM users
        WHERE (email = ? OR phone = ?)
        LIMIT 1
    ");
    $stmt->bind_param("ss", $identifier, $identifier);
    $stmt->execute();
    $res = $stmt->get_result();

    if ($res->num_rows === 0) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid verification attempt.'
        ]);
        exit;
    }

    $user = $res->fetch_assoc();
    $stmt->close();

    if (
        (int)$user['verify_token'] !== (int)$code ||
        time() > (int)$user['verify_expires']
    ) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid or expired verification code.'
        ]);
        exit;
    }

    // 🔑 Generate login token
    $rawToken = bin2hex(random_bytes(32));
    $expiry   = time() + (24 * 3600); // 24h

    $upd = $mysqli->prepare("
        UPDATE users
        SET 
            login_token = ?, 
            login_expiry = ?, 
            verify_token = NULL,
            verify_expires = NULL,
            ip = ?
        WHERE id = ?
    ");
    $upd->bind_param("sisi", $rawToken, $expiry, $ip, $user['id']);
    $upd->execute();
    $upd->close();

    // ⚠️ Notify user if IP changed
    if (!empty($user['ip']) && $user['ip'] !== $ip) {
           $app = $set['app_name'];
  $name = $user['first_name'];
    $email = $user['email'];

         $subject = "New Login Detected";
    $body = "
        <p>Hello {$name},</p>
        <p>A new login was detected on your {$app} account.</p>
        <ul>
            <li><strong>IP Address:</strong> {$ip}</li>
            <li><strong>Time:</strong> " . date('Y-m-d H:i:s') . "</li>
        </ul>
        <p>If this was not you, please change your password immediately.</p>
    ";

    sendMail($email, $subject, $body);
}
    

    echo json_encode([
        'success' => true,
        'message' => 'Login successful.',
        'email'   => $user['email'],
        'token'   => $rawToken,
        'expiry'  => $expiry
    ]);

break;


  case 'get_pass_reset_code':
                $identifier = $_POST['identifier'];
                $code = rand(1000, 9999);

                // Validate input
                if (empty($identifier)) {
                    echo json_encode(['success' => false, 'message' => 'Please fill in all fields.']);
                    exit;
                }
            
                // Prepare SQL query to check for the user
                $query = $mysqli->prepare("SELECT * FROM users WHERE email = ? OR phone = ?");
                $query->bind_param("ss", $identifier, $identifier);
                $query->execute();
                $result = $query->get_result();
                
                if ($result->num_rows > 0) {
                    $user = $result->fetch_assoc();
                    $email = $user['email'];
                    $name = $user['first_name'];
                    $mysqli->query("UPDATE users SET verify_token = '$code' WHERE email = '$email'");
                    sendMail($email, "Your password reset code", "Hey $name, <br>Please use <b>$code</b> as your verification code. DO NOT SHARE THIS CODE WITH ANYONE ELSE.");
                    echo json_encode(['success' => true, 'message' => 'Code sent to '.$email,  "email" => $email]);
                } else {
                    echo json_encode(['success' => false, 'message' => 'User not found.']);
                }
                break;


 case 'get_v_code':
    $email = sanitizeInput($_POST['email'] ?? '', 'email');
    $code = rand(10000, 99999);
 $stmt = $mysqli->prepare("
                UPDATE users 
                SET verify_token = ? 
                WHERE email = ?
            ");
            $stmt->bind_param("is", $code, $email);
            if (!$stmt->execute() || $stmt->affected_rows === 0) {
                 echo json_encode(['success' => false, 'message' => 'Request failed.']);
        exit;
            }
            $stmt->close();
  $app = $set['app_name'];
        $subject = "Your verification code";

        $html = "
            <div style='font-family:Arial,sans-serif;max-width:600px;margin:auto;padding:20px;background:#f9f9f9;border-radius:8px;'>
                <h2 style='color:#1584d4;'>Complete your request!</h2>
                <p>Your verification code is:</p>
                <h1 style='color:#333;background:#e3f2fd;padding:10px;border-radius:6px;text-align:center;'>
                    {$code}
                </h1>
                <p>Please enter this code in the app to complete your request.</p>
                <hr>
                <p style='font-size:13px;color:#666;'>© " . date('Y') . " {$app}. All rights reserved.</p>
            </div>
        ";

        sendMail($email, $subject, $html);

        echo json_encode([
            'success' => true,
            'message' => 'Verification code sent.',
          //  'code'   => $code,
          //  'email'   => $email
        ]);

                break;


case 'resend_v_email':

    $email = sanitizeInput($_POST['email'] ?? '', 'email');

    if (!$email) {
        echo json_encode([
            'success' => false,
            'message' => 'Email is required.'
        ]);
        exit;
    }

    // 1️⃣ Find user
    $stmt = $mysqli->prepare("SELECT id, first_name, email_verified, verify_token FROM users WHERE email = ? LIMIT 1");
    if (!$stmt) {
        echo json_encode([
            'success' => false,
            'message' => 'Error happened: '
        ]);
        exit;
    }

    $stmt->bind_param("s", $email);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows === 0) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid email.'
        ]);
        $stmt->close();
        exit;
    }

    $user = $result->fetch_assoc();
    $stmt->close();

 

    // 3️⃣ Use existing verification code (same behavior as Node.js version)
    $newToken = $user['verify_token'];

    if (!$newToken) {
        echo json_encode([
            'success' => false,
            'message' => 'Verification token missing. Please contact support.'
        ]);
        exit;
    }

    // App Name (same way you use it in your other code)
    $appName = $set['app_name'];

    // 4️⃣ Build HTML email
    $html = "
        <h2>New Email Verification Code</h2>
        <p>Hi " . htmlspecialchars($user['first_name']) . ",</p>
        <p>Use the following 4-digit code to verify your email:</p>
        <h1 style='letter-spacing:5px;'>" . htmlspecialchars($newToken) . "</h1>
        <p>If you didn’t request this, please ignore the message.</p>
        <br>
        <p style='color:#888;font-size:12px;'>© " . date("Y") . " $appName</p>
    ";

    // Send email
    if (sendMail($email, "Your New Verification Code", $html)) {
        echo json_encode([
            'success' => true,
            'message' => 'Verification code resent successfully.'
        ]);
    } else {
        echo json_encode([
            'success' => false,
            'message' => 'Failed to send verification code. Please try again.'
        ]);
    }

    break;

    case 'verify_code':

    $email = sanitizeInput($_POST['email'] ?? '', 'email');
    $code  = trim($_POST['code'] ?? '');

    if (!$email) {
        echo json_encode([
            'success' => false,
            'message' => 'Email is required.'
        ]);
        exit;
    }

    // Code must be digits but kept as STRING
    if ($code === '' || !ctype_digit($code)) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid code.'
        ]);
        exit;
    }

    // 1️⃣ Find user
    $stmt = $mysqli->prepare("
        SELECT id, verify_token 
        FROM users 
        WHERE email = ? 
        LIMIT 1
    ");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows === 0) {
        echo json_encode([
            'success' => false,
            'message' => 'Invalid email.'
        ]);
        exit;
    }

    $user = $result->fetch_assoc();
    $stmt->close();

    $storedCode = trim((string)$user['verify_token']);

    if ($storedCode === '') {
        echo json_encode([
            'success' => false,
            'message' => 'Verification token missing.'
        ]);
        exit;
    }

    // STRICT STRING COMPARISON
    if ($storedCode !== $code) {
        echo json_encode([
            'success' => false,
            'message' => 'Incorrect code.'
        ]);
        exit;
    }

    echo json_encode([
        'success' => true,
        'message' => 'Verified.'
    ]);

break;


   case 'verify_email':

    $code = sanitizeInput($_POST['code'] ?? '');

    if (!$code) {
        echo json_encode([
            'success' => false,
            'message' => "Verification code required."
        ]);
        exit;
    }

    // 1️⃣ Check if a user exists with this verify code
    $stmt = $mysqli->prepare("SELECT id, email_verified, verify_token FROM users WHERE verify_token = ? LIMIT 1");
    if (!$stmt) {
        echo json_encode([
            'success' => false,
            'message' => "Database error: "
        ]);
        exit;
    }

    $stmt->bind_param("s", $code);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows === 0) {
        echo json_encode([
            'success' => false,
            'message' => "Invalid or expired verification code."
        ]);
        $stmt->close();
        exit;
    }

    $user = $result->fetch_assoc();
    $stmt->close();

   

    // 3️⃣ Mark email as verified
    $stmt2 = $mysqli->prepare("UPDATE users SET email_verified = 1, verify_token = NULL WHERE id = ?");
    if (!$stmt2) {
        echo json_encode([
            'success' => false,
            'message' => "Database error: "
        ]);
        exit;
    }

    $stmt2->bind_param("i", $user['id']);
    if ($stmt2->execute()) {
        echo json_encode([
            'success' => true,
            'message' => "Email verified successfully."
        ]);
    } else {
        echo json_encode([
            'success' => false,
            'message' => "Failed to verify email. Please try again."
        ]);
    }

    $stmt2->close();
    break;


  case 'get_user_info':

   $token = getBearerToken();

    if (!$token || !verifyAuthToken($token)) {
      echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $stmt = $mysqli->prepare("SELECT * FROM users WHERE login_token = ? LIMIT 1");
    $stmt->bind_param("s", $token);
    $stmt->execute();
    $result = $stmt->get_result();
    $user = $result->fetch_assoc();

    if (!$user) {
        echo json_encode(['success' => false, 'message' => 'Invalid token.']);
        exit;
    }

    // ✅ Check expiry
    if (time() > (int)$user['login_expiry']) {
        // Expired — clear token
        $clear = $mysqli->prepare("UPDATE users SET login_token = NULL, login_expiry = NULL WHERE id = ?");
        $clear->bind_param("i", $user['id']);
        $clear->execute();

        echo json_encode(['success' => false,'message' => 'Session expired. Please log in again.']);
        exit;
    }

    // Valid
    unset($user['password']); 
   // $rec[] = $user;
    echo json_encode([$user]);
    break;

             case 'get_tx_user_info':

    $token = getBearerToken();
    if (!$token || !verifyAuthToken($token)) {
        echo json_encode(['success' => false, 'message' => 'Invalid request session.']);
        exit;
    }

    $email = sanitizeInput($_POST['email'] ?? '', 'email');

    if (!$email) {
        echo json_encode(['error' => 'Invalid request identity.']);
        exit;
    }

    $stmt = $mysqli->prepare("
        SELECT first_name, last_name 
        FROM users 
        WHERE email = ? 
        LIMIT 1
    ");
    $stmt->bind_param("s", $email);
    $stmt->execute();

    $res = $stmt->get_result();
    $user = $res->fetch_assoc();
    $stmt->close();

    if (!$user) {
        echo json_encode(['error' => 'User not found.']);
        exit;
    }

    echo json_encode([$user]);
    break;


              case 'set_pin':

    $email = $_POST['email'] ?? '';
    $pin   = trim($_POST['pin']) ?? '';

    // ✅ Validate PIN: exactly 4 digits
    if (!preg_match('/^\d{4}$/', $pin)) {
        echo json_encode([
            'success' => false,
            'message' => 'Transaction PIN must be exactly 4 digits.'
        ]);
        exit;
    }

    $sql = "
        UPDATE users 
        SET tx_pin = ? 
        WHERE (email = ? OR login_token = ?)
          AND (tx_pin IS NULL OR tx_pin = '')
    ";

    if ($stmt = $mysqli->prepare($sql)) {
        $stmt->bind_param("sss", $pin, $email, $email);

        if ($stmt->execute()) {
            if ($stmt->affected_rows > 0) {
                echo json_encode([
                    'success' => true,
                    'message' => 'Your new PIN has been set successfully!'
                ]);
            } else {
                echo json_encode([
                    'success' => false,
                    'message' => 'PIN already set or user not found.'
                ]);
            }
        } else {
            echo json_encode([
                'success' => false,
                'message' => 'Execution failed.'
            ]);
        }

        $stmt->close();
    } else {
        echo json_encode([
            'success' => false,
            'message' => 'Database error.'
        ]);
    }

    break;


        case 'change_password':
                        $email = sanitizeInput($_POST['email'], 'email');
                        $pin = sanitizeInput($_POST['pin']);
                        $code = sanitizeInput($_POST['code'], 'int');
                         if(!$code){
                            echo json_encode(['success' => false, 'message' => 'Invalid request.']);
                            exit;
                        }
                        if(strlen($pin) < 8){
                            echo json_encode(['success' => false, 'message' => 'Password must have a minimum of 8 characters.']);
                            exit;
                        }
                         $u = getUserInfo($email);
          if (!$u) {
        echo json_encode(['success' => false, 'message' => ' Sender not found.']);
        exit;
    }
      if (!$u['verify_token']) {
        echo json_encode(['success' => false, 'message' => 'Invalid request.']);
        exit;
    }
      if ($u['verify_token'] != $code) {
        echo json_encode(['success' => false, 'message' => 'Invalid request or access.']);
        exit;
    }
            $userId = $u['id'];

                        $pass = password_hash($pin, PASSWORD_DEFAULT);
                        $sql = "UPDATE users SET password = ?, verify_token = NULL WHERE id = ?";
    if ($stmt = $mysqli->prepare($sql)) {
        $stmt->bind_param("si", $pass, $userId );
        
        if ($stmt->execute()) {
            if ($stmt->affected_rows > 0) {
                echo json_encode(['success' => true, 'message' => 'Your new password has been set successfully!']);
            } else {
                echo json_encode(['success' => false, 'message' => 'No user found with the provided info or you are setting same password as the current one.']);
            }
        } else {
            echo json_encode(['success' => false, 'message' => 'Failed to execute the statement: ' . $stmt->error]);
        }
        
        $stmt->close();
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to prepare the statement: ' . $mysqli->error]);
    }
                        break;


case 'record_tx_and_debit':
    $title   = sanitizeInput($_POST['title']);
    $desc    = sanitizeInput($_POST['desc']);
    $time    = time();
    $amount  = sanitizeInput($_POST['amount'], 'float');
    $ref     = sanitizeInput($_POST['ref']);
    $our_ref = sanitizeInput($_POST['our_ref']);
    $status  = sanitizeInput($_POST['status']);
    $email   = sanitizeInput($_POST['email'], 'email');
    $type    = sanitizeInput($_POST['type']);
    $payload = isset($_POST['payload']) ? $_POST['payload'] : null;

    try {
        $u = getUserInfo($email);
        $userId = $u['id'];

        $transactionQuery = $mysqli->prepare("
            INSERT INTO transactions 
            (flow, currency, payload, title, description, date, amount, ref, status, our_ref, email, user_id, type) 
            VALUES ('out', 'NGN', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");
        $transactionQuery->bind_param(
            "sssssssssis",
            $payload,
            $title,
            $desc,
            $time,
            $amount,
            $ref,
            $status,
            $our_ref,
            $email,
            $userId,
            $type
        );
        if (!$transactionQuery->execute()) {
            throw new Exception("Transaction record failed: " . $transactionQuery->error);
        }

        echo json_encode(['success' => true, 'message' => 'Transaction recorded.']);
    } catch (Exception $e) {
        echo json_encode(['success' => false, 'message' => 'Transaction failed: ' . $e->getMessage()]);
    }

    if (isset($transactionQuery)) $transactionQuery->close();
    break;



            case 'get_settings':
                 $set = getSettings()[0];
                  echo json_encode([
        "success" => true,
        "data" => $set,
    ]);
                   break;

                    case "get_paged_users":

    $page = intval($_POST['page'] ?? 1);
    $limit = intval($_POST['limit'] ?? 10);
    $offset = ($page - 1) * $limit;

    // Count total
    $countQ = $mysqli->query("SELECT COUNT(*) AS total FROM users");
    $total = $countQ->fetch_assoc()['total'];
    $total_pages = ceil($total / $limit);

    $stmt = $mysqli->prepare("
        SELECT id, email, ngn_balance, first_name, phone, reg_date 
        FROM users 
        ORDER BY id DESC 
        LIMIT ? OFFSET ?
    ");

    $stmt->bind_param("ii", $limit, $offset);
    $stmt->execute();
    $result = $stmt->get_result();

    $users = [];
    while ($row = $result->fetch_assoc()) {
        $users[] = $row;
    }

    echo json_encode([
        "success" => true,
        "users" => $users,
        "total_pages" => $total_pages
    ]);
    break;


    }
    
    

   
}

function getUserIdByEmail($email) {
    global $mysqli;
    $userId = null;

    $userQuery = $mysqli->prepare("SELECT id FROM users WHERE email = ?");
    $userQuery->bind_param("s", $email);
    $userQuery->execute();
    $userQuery->bind_result($userId);
    $userQuery->fetch();
    $userQuery->close();

    return $userId;
}

?>
