<?php
require_once __DIR__ . '/bootstrap.php';
// endpoints/admin.php — all /admin/* routes

$auth = requireAdmin();
$db   = Database::getInstance();

// ── DASHBOARD ──────────────────────────────────────────────
if ($method === 'GET' && strpos($uri, '/admin/dashboard') !== false) {
    $stats = [
        'total_users'        => $db->fetch("SELECT COUNT(*) as c FROM users")['c'],
        'active_users'       => $db->fetch("SELECT COUNT(*) as c FROM users WHERE status='active'")['c'],
        'total_deposits'     => $db->fetch("SELECT COALESCE(SUM(amount),0) as s FROM deposits WHERE status='approved'")['s'],
        'pending_deposits'   => $db->fetch("SELECT COUNT(*) as c FROM deposits WHERE status='pending'")['c'],
        'total_withdrawals'  => $db->fetch("SELECT COALESCE(SUM(amount),0) as s FROM withdrawals WHERE status='approved'")['s'],
        'pending_withdrawals'=> $db->fetch("SELECT COUNT(*) as c FROM withdrawals WHERE status='pending'")['c'],
        'active_investments' => $db->fetch("SELECT COUNT(*) as c FROM investments WHERE status='active'")['c'],
        'total_invested'     => $db->fetch("SELECT COALESCE(SUM(amount),0) as s FROM investments")['s'],
        'pending_kyc'        => $db->fetch("SELECT COUNT(*) as c FROM kyc_documents WHERE status='pending'")['c'],
        'open_tickets'       => $db->fetch("SELECT COUNT(*) as c FROM tickets WHERE status='open'")['c'],
        'new_users_today'    => $db->fetch("SELECT COUNT(*) as c FROM users WHERE DATE(created_at)=CURDATE()")['c'],
    ];

    // Recent deposits
    $recentDeposits = $db->fetchAll(
        "SELECT d.*, u.username, u.email FROM deposits d JOIN users u ON d.user_id=u.id ORDER BY d.created_at DESC LIMIT 5"
    );
    // Recent withdrawals
    $recentWithdrawals = $db->fetchAll(
        "SELECT w.*, u.username, u.email FROM withdrawals w JOIN users u ON w.user_id=u.id ORDER BY w.created_at DESC LIMIT 5"
    );
    // New users
    $newUsers = $db->fetchAll(
        "SELECT id,username,email,first_name,last_name,country,status,created_at FROM users ORDER BY created_at DESC LIMIT 5"
    );

    jsonSuccess(['stats'=>$stats,'recent_deposits'=>$recentDeposits,'recent_withdrawals'=>$recentWithdrawals,'new_users'=>$newUsers]);
}

// ── USERS ──────────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/users$#', $uri)) {
    $page   = max(1,(int)($_GET['page']??1));
    $limit  = 20; $offset = ($page-1)*$limit;
    $search = sanitize($_GET['search']??'');
    $where  = $search ? "WHERE username LIKE ? OR email LIKE ?" : '';
    $params = $search ? ["%$search%","%$search%"] : [];
    $total  = $db->fetch("SELECT COUNT(*) as c FROM users $where", $params)['c'];
    $users  = $db->fetchAll("SELECT id,username,email,first_name,last_name,country,status,kyc_status,deposit_balance,interest_balance,total_invested,total_referrals,created_at FROM users $where ORDER BY created_at DESC LIMIT $limit OFFSET $offset", $params);
    jsonSuccess(['users'=>$users,'total'=>(int)$total,'page'=>$page,'pages'=>ceil($total/$limit)]);
}

// GET /admin/user/{id}
if ($method === 'GET' && preg_match('#^/admin/user/(\d+)$#', $uri, $m)) {
    $uid  = (int)$m[1];
    $user = $db->fetch("SELECT * FROM users WHERE id=?", [$uid]);
    if (!$user) jsonError('User not found.',404);
    unset($user['password']);
    $investments = $db->fetchAll("SELECT i.*,p.name as plan_name FROM investments i JOIN plans p ON i.plan_id=p.id WHERE i.user_id=? ORDER BY i.created_at DESC LIMIT 20", [$uid]);
    $deposits    = $db->fetchAll("SELECT * FROM deposits WHERE user_id=? ORDER BY created_at DESC LIMIT 20", [$uid]);
    $withdrawals = $db->fetchAll("SELECT * FROM withdrawals WHERE user_id=? ORDER BY created_at DESC LIMIT 20", [$uid]);
    $txs         = $db->fetchAll("SELECT * FROM transactions WHERE user_id=? ORDER BY created_at DESC LIMIT 20", [$uid]);
    jsonSuccess(['user'=>$user,'investments'=>$investments,'deposits'=>$deposits,'withdrawals'=>$withdrawals,'transactions'=>$txs]);
}

// POST /admin/user/{id} — adjust balance / suspend / activate (used by admin SPA)
if ($method === 'POST' && preg_match('#^/admin/user/(\d+)$#', $uri, $m)) {
    $uid    = (int)$m[1];
    $action = sanitize($input['action'] ?? '');
    if ($action === 'adjust_balance') {
        $wallet = in_array($input['wallet']??'',['deposit','interest']) ? $input['wallet'] : 'deposit';
        $amount = (float)($input['amount'] ?? 0);
        if ($amount === 0.0) jsonError('Amount cannot be zero.');
        $col = $wallet === 'interest' ? 'interest_balance' : 'deposit_balance';
        $db->query("UPDATE users SET $col = $col + ? WHERE id=?", [$amount, $uid]);
        $txType = $amount > 0 ? 'admin_credit' : 'admin_debit';
        $db->insert("INSERT INTO transactions (user_id,type,amount,description,wallet,status) VALUES (?,?,?,?,?,'completed')",
            [$uid, $txType, abs($amount), 'Admin balance adjustment', $wallet]);
        jsonSuccess([], 'Balance updated successfully.');
    } elseif ($action === 'suspend') {
        $db->query("UPDATE users SET status='suspended' WHERE id=?", [$uid]);
        jsonSuccess([], 'User suspended.');
    } elseif ($action === 'activate') {
        $db->query("UPDATE users SET status='active' WHERE id=?", [$uid]);
        jsonSuccess([], 'User activated.');
    } elseif ($action === 'ban') {
        $db->query("UPDATE users SET status='banned' WHERE id=?", [$uid]);
        jsonSuccess([], 'User banned.');
    } else { jsonError('Invalid action.'); }
}

// PUT /admin/user/{id} — update user balance / status
if ($method === 'PUT' && preg_match('#^/admin/user/(\d+)$#', $uri, $m)) {
    $uid    = (int)$m[1];
    $fields = [];
    $params = [];
    if (isset($input['status'])) { $fields[]='status=?'; $params[]=sanitize($input['status']); }
    if (isset($input['deposit_balance'])) { $fields[]='deposit_balance=?'; $params[]=(float)$input['deposit_balance']; }
    if (isset($input['interest_balance'])) { $fields[]='interest_balance=?'; $params[]=(float)$input['interest_balance']; }
    if (isset($input['kyc_status'])) { $fields[]='kyc_status=?'; $params[]=sanitize($input['kyc_status']); }
    if (!$fields) jsonError('Nothing to update.');
    $params[] = $uid;
    $db->query("UPDATE users SET ".implode(',',$fields)." WHERE id=?", $params);
    jsonSuccess([], 'User updated.');
}

// ── DEPOSITS ──────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/deposits$#', $uri)) {
    $page   = max(1,(int)($_GET['page']??1));
    $limit  = 20; $offset=($page-1)*$limit;
    $status = sanitize($_GET['status']??'');
    $where  = $status ? "WHERE d.status=?" : '';
    $params = $status ? [$status] : [];
    $total  = $db->fetch("SELECT COUNT(*) as c FROM deposits d $where", $params)['c'];
    $deps   = $db->fetchAll("SELECT d.*,u.username,u.email FROM deposits d JOIN users u ON d.user_id=u.id $where ORDER BY d.created_at DESC LIMIT $limit OFFSET $offset", $params);
    jsonSuccess(['deposits'=>$deps,'total'=>(int)$total,'page'=>$page,'pages'=>ceil($total/$limit)]);
}

// POST /admin/deposit/{id}/action
if ($method === 'POST' && preg_match('#^/admin/deposit/(\d+)/action$#', $uri, $m)) {
    $depId  = (int)$m[1];
    $action = sanitize($input['action'] ?? ''); // 'approve' or 'reject'
    $note   = sanitize($input['note'] ?? '');

    $dep = $db->fetch("SELECT * FROM deposits WHERE id=?", [$depId]);
    if (!$dep) jsonError('Deposit not found.',404);
    if ($dep['status'] !== 'pending') jsonError('Deposit already processed.');

    if ($action === 'approve') {
        $db->query("UPDATE deposits SET status='approved', admin_note=? WHERE id=?", [$note, $depId]);
        $db->query("UPDATE users SET deposit_balance=deposit_balance+? WHERE id=?", [$dep['amount'], $dep['user_id']]);
        $db->query("UPDATE transactions SET status='completed', description=? WHERE reference_id=? AND type='deposit'",
            ["Deposit of \${$dep['amount']} via {$dep['currency']} (Approved)", $depId]);
        $db->insert("INSERT INTO transactions (user_id,type,amount,description,reference_id,wallet,status) VALUES (?,?,?,?,?,?,?)",
            [$dep['user_id'],'deposit',$dep['amount'],"Deposit approved: \${$dep['amount']} {$dep['currency']}",$depId,'deposit','completed']);
        jsonSuccess([], 'Deposit approved and balance credited.');
    } elseif ($action === 'reject') {
        $db->query("UPDATE deposits SET status='rejected', admin_note=? WHERE id=?", [$note, $depId]);
        $db->query("UPDATE transactions SET status='failed' WHERE reference_id=? AND type='deposit'", [$depId]);
        jsonSuccess([], 'Deposit rejected.');
    } else {
        jsonError('Invalid action.');
    }
}

// ── WITHDRAWALS ────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/withdrawals$#', $uri)) {
    $page   = max(1,(int)($_GET['page']??1));
    $limit  = 20; $offset=($page-1)*$limit;
    $status = sanitize($_GET['status']??'');
    $where  = $status ? "WHERE w.status=?" : '';
    $params = $status ? [$status] : [];
    $total  = $db->fetch("SELECT COUNT(*) as c FROM withdrawals w $where", $params)['c'];
    $wds    = $db->fetchAll("SELECT w.*,u.username,u.email FROM withdrawals w JOIN users u ON w.user_id=u.id $where ORDER BY w.created_at DESC LIMIT $limit OFFSET $offset", $params);
    jsonSuccess(['withdrawals'=>$wds,'total'=>(int)$total,'page'=>$page,'pages'=>ceil($total/$limit)]);
}

// POST /admin/withdrawal/{id}/action
if ($method === 'POST' && preg_match('#^/admin/withdrawal/(\d+)/action$#', $uri, $m)) {
    $wdId   = (int)$m[1];
    $action = sanitize($input['action'] ?? '');
    $note   = sanitize($input['note'] ?? '');
    $txHash = sanitize($input['tx_hash'] ?? '');

    $wd = $db->fetch("SELECT * FROM withdrawals WHERE id=?", [$wdId]);
    if (!$wd) jsonError('Withdrawal not found.',404);
    if ($wd['status'] === 'approved' || $wd['status'] === 'rejected') jsonError('Already processed.');

    if ($action === 'approve') {
        $db->query("UPDATE withdrawals SET status='approved', admin_note=?, tx_hash=? WHERE id=?", [$note, $txHash, $wdId]);
        $db->query("UPDATE transactions SET status='completed' WHERE reference_id=? AND type='withdrawal'", [$wdId]);
        $db->query("UPDATE users SET total_withdrawn=total_withdrawn+? WHERE id=?", [$wd['amount'], $wd['user_id']]);
        jsonSuccess([], 'Withdrawal approved.');
    } elseif ($action === 'reject') {
        // Refund the balance
        $col = $wd['wallet_from'] === 'interest' ? 'interest_balance' : 'deposit_balance';
        $db->query("UPDATE users SET $col=$col+? WHERE id=?", [$wd['amount'], $wd['user_id']]);
        $db->query("UPDATE withdrawals SET status='rejected', admin_note=? WHERE id=?", [$note, $wdId]);
        $db->query("UPDATE transactions SET status='failed' WHERE reference_id=? AND type='withdrawal'", [$wdId]);
        jsonSuccess([], 'Withdrawal rejected and funds refunded.');
    } else {
        jsonError('Invalid action.');
    }
}

// ── INVESTMENTS ────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/investments$#', $uri)) {
    $page   = max(1,(int)($_GET['page']??1));
    $limit  = 20; $offset=($page-1)*$limit;
    $status = sanitize($_GET['status']??'');
    $where  = $status ? "WHERE i.status=?" : '';
    $params = $status ? [$status] : [];
    $total  = $db->fetch("SELECT COUNT(*) as c FROM investments i $where", $params)['c'];
    $invs   = $db->fetchAll("SELECT i.*,u.username,u.email,p.name as plan_name FROM investments i JOIN users u ON i.user_id=u.id JOIN plans p ON i.plan_id=p.id $where ORDER BY i.created_at DESC LIMIT $limit OFFSET $offset", $params);
    jsonSuccess(['investments'=>$invs,'total'=>(int)$total,'page'=>$page,'pages'=>ceil($total/$limit)]);
}

// ── PLANS ──────────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/plans$#', $uri)) {
    jsonSuccess(['plans' => $db->fetchAll("SELECT * FROM plans ORDER BY min_deposit ASC")]);
}

if ($method === 'POST' && preg_match('#^/admin/plans$#', $uri)) {
    $fields = ['name','tier','roi','duration','roi_type','min_deposit','max_deposit','referral_bonus'];
    $vals   = [];
    foreach ($fields as $f) $vals[$f] = $input[$f] ?? null;
    $db->insert("INSERT INTO plans (name,tier,roi,duration,roi_type,min_deposit,max_deposit,referral_bonus) VALUES (?,?,?,?,?,?,?,?)",
        array_values($vals));
    jsonSuccess([], 'Plan created.');
}

if ($method === 'PUT' && preg_match('#^/admin/plans/(\d+)$#', $uri, $m)) {
    $id     = (int)$m[1];
    $fields = []; $params = [];
    foreach (['name','tier','roi','duration','roi_type','min_deposit','max_deposit','referral_bonus','status'] as $f) {
        if (isset($input[$f])) { $fields[]="$f=?"; $params[]=$input[$f]; }
    }
    if (!$fields) jsonError('Nothing to update.');
    $params[]=$id;
    $db->query("UPDATE plans SET ".implode(',',$fields)." WHERE id=?", $params);
    jsonSuccess([], 'Plan updated.');
}

// ── WALLET ADDRESSES ───────────────────────────────────────
// ── ADD new wallet address ─────────────────────────────────
if ($method === 'POST' && preg_match('#^/admin/wallets/add$#', $uri)) {
    $currency = strtoupper(sanitize($input['currency'] ?? ''));
    $network  = sanitize($input['network']  ?? '');
    $address  = sanitize($input['address']  ?? '');

    if (!$currency) jsonError('Currency is required.');
    if (!$network)  jsonError('Network is required.');
    if (!$address)  jsonError('Wallet address is required.');

    // Check for duplicate currency+network
    $existing = $db->fetch("SELECT id FROM wallet_addresses WHERE currency=? AND network=?", [$currency, $network]);
    if ($existing) jsonError("A wallet for $currency on $network already exists. Use Edit to update it.");

    $id = $db->insert(
        "INSERT INTO wallet_addresses (currency, network, address, status) VALUES (?,?,?,1)",
        [$currency, $network, $address]
    );
    jsonSuccess(['id' => $id], "Wallet address added.");
}

if ($method === 'GET' && preg_match('#^/admin/wallets$#', $uri)) {
    $wallets = $db->fetchAll("SELECT * FROM wallet_addresses ORDER BY id ASC");
    // Add public QR URL if file exists
    foreach ($wallets as &$w) {
        if (!empty($w['qr_code'])) {
            // Build QR URL using actual request host as fallback if SITE_URL is placeholder
            $siteUrl = SITE_URL;
            if (empty($siteUrl) || $siteUrl === 'https://your-backend-domain.com') {
                $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
                $host   = $_SERVER['HTTP_HOST'] ?? 'localhost';
                $siteUrl = $scheme . '://' . $host;
            }
            $w['qr_url'] = rtrim($siteUrl, '/') . '/backend/uploads/qr/' . $w['qr_code'];
        } else {
            $w['qr_url'] = null;
        }
    }
    jsonSuccess(['wallets' => $wallets]);
}

if (($method === 'PUT' || $method === 'POST') && preg_match('#^/admin/wallets$#', $uri)) {
    // Detect multipart (QR upload) vs JSON
    $isMultipart = isset($_POST['id']) || !empty($_FILES);

    if ($isMultipart) {
        // FormData submission (may include QR code image)
        $id      = (int)($_POST['id'] ?? 0);
        $address = sanitize($_POST['address'] ?? '');
        if (!$id) jsonError('Wallet ID required.');

        $qrPath = null;
        if (!empty($_FILES['qr_code']) && $_FILES['qr_code']['error'] === UPLOAD_ERR_OK) {
            $uploadDir = __DIR__ . '/../../uploads/qr/';
            if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
            $allowed = ['image/jpeg','image/png','image/gif','image/webp'];
            if (!in_array($_FILES['qr_code']['type'], $allowed))
                jsonError('QR code must be a JPG or PNG image.');
            $ext  = pathinfo($_FILES['qr_code']['name'], PATHINFO_EXTENSION) ?: 'png';
            $name = 'qr_' . $id . '_' . time() . '.' . $ext;
            if (move_uploaded_file($_FILES['qr_code']['tmp_name'], $uploadDir . $name)) {
                $qrPath = $name;
            }
        }

        if ($qrPath) {
            $db->query("UPDATE wallet_addresses SET address=?, qr_code=? WHERE id=?",
                [$address, $qrPath, $id]);
        } else {
            $db->query("UPDATE wallet_addresses SET address=? WHERE id=?",
                [$address, $id]);
        }
        jsonSuccess([], 'Wallet address updated.');
    }

    // JSON body — single or bulk
    if (!empty($input['id']) && isset($input['address'])) {
        $db->query("UPDATE wallet_addresses SET address=? WHERE id=?",
            [sanitize($input['address']), (int)$input['id']]);
        jsonSuccess([], 'Wallet address updated.');
    }
    $wallets = $input['wallets'] ?? [];
    foreach ($wallets as $w) {
        if (!empty($w['id']) && isset($w['address'])) {
            $db->query("UPDATE wallet_addresses SET address=? WHERE id=?",
                [sanitize($w['address']), (int)$w['id']]);
        }
    }
    jsonSuccess([], 'Wallet addresses updated.');
}

// ── KYC ────────────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/kyc$#', $uri)) {
    $status = sanitize($_GET['status']??'pending');
    $docs   = $db->fetchAll("SELECT k.*,u.username,u.email FROM kyc_documents k JOIN users u ON k.user_id=u.id WHERE k.status=? ORDER BY k.created_at DESC", [$status]);
    jsonSuccess(['kyc' => $docs, 'documents' => $docs]);
}

if ($method === 'POST' && preg_match('#^/admin/kyc/(\d+)/action$#', $uri, $m)) {
    $docId  = (int)$m[1];
    $action = sanitize($input['action'] ?? '');
    $note   = sanitize($input['note'] ?? '');
    $doc    = $db->fetch("SELECT * FROM kyc_documents WHERE id=?", [$docId]);
    if (!$doc) jsonError('Document not found.',404);
    if ($action === 'approve') {
        $db->query("UPDATE kyc_documents SET status='approved', admin_note=? WHERE id=?", [$note,$docId]);
        $db->query("UPDATE users SET kyc_status='approved' WHERE id=?", [$doc['user_id']]);
        jsonSuccess([], 'KYC approved.');
    } elseif ($action === 'reject') {
        $db->query("UPDATE kyc_documents SET status='rejected', admin_note=? WHERE id=?", [$note,$docId]);
        $db->query("UPDATE users SET kyc_status='rejected' WHERE id=?", [$doc['user_id']]);
        jsonSuccess([], 'KYC rejected.');
    } else { jsonError('Invalid action.'); }
}

// ── SUPPORT TICKETS ────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/tickets$#', $uri)) {
    $status = sanitize($_GET['status'] ?? '');
    if ($status) {
        $tickets = $db->fetchAll("SELECT t.*,u.username,u.email FROM tickets t JOIN users u ON t.user_id=u.id WHERE t.status=? ORDER BY t.updated_at DESC", [$status]);
    } else {
        $tickets = $db->fetchAll("SELECT t.*,u.username,u.email FROM tickets t JOIN users u ON t.user_id=u.id ORDER BY t.updated_at DESC LIMIT 100");
    }
    jsonSuccess(['tickets' => $tickets]);
}

if ($method === 'GET' && preg_match('#^/admin/ticket/(\d+)$#', $uri, $m)) {
    $tid     = (int)$m[1];
    $ticket  = $db->fetch("SELECT t.*,u.username,u.email FROM tickets t JOIN users u ON t.user_id=u.id WHERE t.id=?", [$tid]);
    if (!$ticket) jsonError('Ticket not found.',404);
    $messages= $db->fetchAll("SELECT * FROM ticket_messages WHERE ticket_id=? ORDER BY created_at ASC", [$tid]);
    jsonSuccess(['ticket'=>$ticket,'messages'=>$messages]);
}

if ($method === 'POST' && preg_match('#^/admin/ticket/(\d+)$#', $uri, $m)) {
    $tid    = (int)$m[1];
    $action = sanitize($input['action'] ?? 'reply');
    $msg    = sanitize($input['message'] ?? '');
    if ($action === 'reply') {
        if (!$msg) jsonError('Message required.');
        $db->insert("INSERT INTO ticket_messages (ticket_id,sender,message) VALUES (?,?,?)", [$tid,'admin',$msg]);
        $db->query("UPDATE tickets SET status='replied', updated_at=NOW() WHERE id=?", [$tid]);
        jsonSuccess([], 'Reply sent.');
    } elseif ($action === 'close') {
        $db->query("UPDATE tickets SET status='closed' WHERE id=?", [$tid]);
        jsonSuccess([], 'Ticket closed.');
    } else { jsonError('Invalid action.'); }
}

// ── SETTINGS ───────────────────────────────────────────────
if ($method === 'GET' && preg_match('#^/admin/settings$#', $uri)) {
    $rows = $db->fetchAll("SELECT * FROM settings");
    $settings = [];
    foreach ($rows as $r) $settings[$r['key']] = $r['value'];
    jsonSuccess(['settings' => $settings]);
}

if (($method === 'PUT' || $method === 'POST') && preg_match('#^/admin/settings$#', $uri)) {
    $allowed = ['site_name','site_email','maintenance_mode','registration_open','min_withdrawal','max_withdrawal','referral_bonus_pct'];
    foreach ($allowed as $k) {
        if (isset($input[$k])) {
            $db->query("INSERT INTO settings (`key`,`value`) VALUES (?,?) ON DUPLICATE KEY UPDATE `value`=?", [$k, $input[$k], $input[$k]]);
        }
    }
    jsonSuccess([], 'Settings updated.');
}

// ── CRON: Process Completed Investments ────────────────────
if (($method === 'POST' || $method === 'GET') && strpos($uri, '/admin/cron/process-investments') !== false) {
    // This should be called by a cron job every hour
    // or by the admin manually
    $matured = $db->fetchAll(
        "SELECT i.*, p.capital_back FROM investments i JOIN plans p ON i.plan_id=p.id WHERE i.status='active' AND i.expires_at <= NOW()"
    );
    $processed = 0;
    foreach ($matured as $inv) {
        $payout = $inv['capital_back'] ? $inv['total_return'] : $inv['roi_amount'];
        $db->query("UPDATE users SET interest_balance=interest_balance+? WHERE id=?", [$payout, $inv['user_id']]);
        $db->query("UPDATE investments SET status='completed', completed_at=NOW() WHERE id=?", [$inv['id']]);
        $db->insert(
            "INSERT INTO transactions (user_id,type,amount,description,reference_id,wallet,status) VALUES (?,?,?,?,?,?,?)",
            [$inv['user_id'],'roi',$payout,"Investment #{$inv['id']} matured — payout: \$$payout",$inv['id'],'interest','completed']
        );
        $processed++;
    }
    jsonSuccess(['processed' => $processed], "$processed investment(s) processed.");
}