<?php
/* ============================================================================
   WBMCZ — Dual wallet engine

   MAIN wallet    (users.wallet_balance)
       - topped up by deposits (Razorpay / admin recharge)
       - withdrawable
       - spendable on anything

   VIRTUAL wallet (users.bonus_wallet)
       - filled by referral earnings and bonuses only
       - NOT withdrawable
       - spendable on subscriptions and product/service purchases only

   Spending rule (chosen by the client):
       Virtual is spent FIRST, the remainder comes out of Main.

   Every movement is written to `wallet_ledger` so admin can audit it and the
   user can see a clean history split by wallet.
   ============================================================================ */

if (!defined('WALLET_MAIN'))    define('WALLET_MAIN', 'main');
if (!defined('WALLET_VIRTUAL')) define('WALLET_VIRTUAL', 'virtual');
if (!defined('WALLET_REPORT'))  define('WALLET_REPORT',  'report');   // statistics only, never a balance

/* ---------------------------------------------------------------------------
   Schema (self-healing — safe to call on every page load)
   --------------------------------------------------------------------------- */
function wallet_ensure_schema($db) {
    static $done = false;
    if ($done) return;
    $done = true;

    $conn = $db->getConnection();

    foreach ([
        'bonus_wallet'   => "DECIMAL(10,2) NOT NULL DEFAULT 0.00",
        'is_subscribed'  => "TINYINT(1) NOT NULL DEFAULT 0",
        'referred_by'    => "INT NULL DEFAULT NULL",
    ] as $c => $def) {
        try {
            $r = $conn->query("SHOW COLUMNS FROM users LIKE '$c'");
            if ($r && $r->num_rows === 0) { $conn->query("ALTER TABLE users ADD COLUMN $c $def"); }
        } catch (\Throwable $e) { /* ignore */ }
    }

    try {
        $conn->query("CREATE TABLE IF NOT EXISTS wallet_ledger (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            wallet VARCHAR(10) NOT NULL DEFAULT 'main',      -- main | virtual
            direction VARCHAR(10) NOT NULL,                  -- credit | debit
            amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
            balance_after DECIMAL(10,2) NOT NULL DEFAULT 0.00,
            category VARCHAR(40) NOT NULL DEFAULT 'other',   -- deposit|withdrawal|purchase|referral|seller_share|staff_share|subscription|refund|admin|bonus
            description VARCHAR(255) NULL,
            reference_type VARCHAR(30) NULL,                 -- order | service | subscription | transaction
            reference_id INT NULL,
            counterparty_id INT NULL,                        -- the other user involved (e.g. the referred buyer)
            created_by INT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            KEY idx_wl_user (user_id, created_at),
            KEY idx_wl_wallet (wallet),
            KEY idx_wl_cat (category)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    } catch (\Throwable $e) { /* ignore */ }
}

/* ---------------------------------------------------------------------------
   Balances
   --------------------------------------------------------------------------- */
function wallet_balances($db, $user_id) {
    try {
        $r = $db->select("SELECT wallet_balance, bonus_wallet FROM users WHERE id = ?", [(int)$user_id]);
        if (!empty($r)) {
            $m = (float)($r[0]['wallet_balance'] ?? 0);
            $v = (float)($r[0]['bonus_wallet'] ?? 0);
            return ['main' => $m, 'virtual' => $v, 'total' => round($m + $v, 2)];
        }
    } catch (\Throwable $e) {}
    return ['main' => 0.0, 'virtual' => 0.0, 'total' => 0.0];
}

/* What the user can actually pay with right now (virtual + main) */
function wallet_spendable($db, $user_id) {
    $b = wallet_balances($db, $user_id);
    return $b['total'];
}

/* ---------------------------------------------------------------------------
   Ledger writer — always call this, never UPDATE the columns directly
   --------------------------------------------------------------------------- */
function wallet__ledger($db, $user_id, $wallet, $direction, $amount, $balance_after, $opts = []) {
    try {
        $db->insert("INSERT INTO wallet_ledger
            (user_id, wallet, direction, amount, balance_after, category, description, reference_type, reference_id, counterparty_id, created_by)
            VALUES (?,?,?,?,?,?,?,?,?,?,?)", [
            (int)$user_id, $wallet, $direction, round((float)$amount, 2), round((float)$balance_after, 2),
            $opts['category'] ?? 'other',
            $opts['description'] ?? '',
            $opts['reference_type'] ?? null,
            isset($opts['reference_id']) ? (int)$opts['reference_id'] : null,
            isset($opts['counterparty_id']) ? (int)$opts['counterparty_id'] : null,
            isset($opts['created_by']) ? (int)$opts['created_by'] : null,
        ]);
    } catch (\Throwable $e) { /* ignore */ }
}

/* Mirror into the legacy `transactions` table so old screens keep working */
function wallet__legacy($db, $user_id, $direction, $amount, $description) {
    try {
        $db->insert("INSERT INTO transactions (user_id, type, amount, description, status) VALUES (?, ?, ?, ?, 'Success')",
            [(int)$user_id, $direction === 'credit' ? 'credit' : 'debit', round((float)$amount, 2), $description]);
    } catch (\Throwable $e) {}
}

/* ---------------------------------------------------------------------------
   CREDIT
   --------------------------------------------------------------------------- */
/**
 * Put money into one of the wallets.
 * $wallet: WALLET_MAIN (deposits, seller shares, refunds) or WALLET_VIRTUAL (referral, bonus)
 */
function wallet_credit($db, $user_id, $wallet, $amount, $opts = []) {
    wallet_ensure_schema($db);
    $amount = round((float)$amount, 2);
    if ($amount <= 0) return false;

    $col = ($wallet === WALLET_VIRTUAL) ? 'bonus_wallet' : 'wallet_balance';
    $db->insert("UPDATE users SET `$col` = `$col` + ? WHERE id = ?", [$amount, (int)$user_id]); // whitelisted

    $b = wallet_balances($db, $user_id);
    $after = ($wallet === WALLET_VIRTUAL) ? $b['virtual'] : $b['main'];

    wallet__ledger($db, $user_id, $wallet, 'credit', $amount, $after, $opts);
    if (empty($opts['skip_legacy'])) {
        wallet__legacy($db, $user_id, 'credit', $amount, $opts['description'] ?? 'Wallet credit');
    }
    return true;
}

/* Referral / bonus money always lands in the virtual wallet */
function wallet_credit_referral($db, $user_id, $amount, $description, $opts = []) {
    return wallet_credit($db, $user_id, WALLET_VIRTUAL, $amount,
        array_merge(['category' => 'referral', 'description' => $description], $opts));
}

/* ---------------------------------------------------------------------------
   DEBIT
   --------------------------------------------------------------------------- */
/**
 * Spend money: virtual first, then main.
 * Returns ['ok'=>bool, 'virtual'=>float, 'main'=>float, 'total'=>float, 'short'=>float]
 * Nothing is deducted unless the full amount is available.
 */
function wallet_spend($db, $user_id, $amount, $opts = []) {
    wallet_ensure_schema($db);
    $amount = round((float)$amount, 2);
    if ($amount <= 0) return ['ok' => true, 'virtual' => 0.0, 'main' => 0.0, 'total' => 0.0, 'short' => 0.0];

    $b = wallet_balances($db, $user_id);
    if ($b['total'] + 0.001 < $amount) {
        return ['ok' => false, 'virtual' => 0.0, 'main' => 0.0, 'total' => 0.0, 'short' => round($amount - $b['total'], 2)];
    }

    // Virtual wallet may not be usable for every category (e.g. withdrawal)
    $allow_virtual = !isset($opts['allow_virtual']) || $opts['allow_virtual'];
    $from_virtual  = $allow_virtual ? min($b['virtual'], $amount) : 0.0;
    $from_main     = round($amount - $from_virtual, 2);

    if ($from_main > $b['main'] + 0.001) {
        return ['ok' => false, 'virtual' => 0.0, 'main' => 0.0, 'total' => 0.0, 'short' => round($from_main - $b['main'], 2)];
    }

    $desc = $opts['description'] ?? 'Wallet debit';

    if ($from_virtual > 0) {
        $db->insert("UPDATE users SET bonus_wallet = bonus_wallet - ? WHERE id = ?", [$from_virtual, (int)$user_id]);
        $nb = wallet_balances($db, $user_id);
        wallet__ledger($db, $user_id, WALLET_VIRTUAL, 'debit', $from_virtual, $nb['virtual'],
            array_merge($opts, ['description' => $desc . ' (virtual wallet)']));
    }
    if ($from_main > 0) {
        $db->insert("UPDATE users SET wallet_balance = wallet_balance - ? WHERE id = ?", [$from_main, (int)$user_id]);
        $nb = wallet_balances($db, $user_id);
        wallet__ledger($db, $user_id, WALLET_MAIN, 'debit', $from_main, $nb['main'],
            array_merge($opts, ['description' => $desc . ($from_virtual > 0 ? ' (main wallet)' : '')]));
    }

    if (empty($opts['skip_legacy'])) {
        $note = $desc;
        if ($from_virtual > 0 && $from_main > 0) {
            $note .= ' — ₹' . number_format($from_virtual, 2) . ' virtual + ₹' . number_format($from_main, 2) . ' main';
        } elseif ($from_virtual > 0) {
            $note .= ' — paid from virtual wallet';
        }
        wallet__legacy($db, $user_id, 'debit', $amount, $note);
    }

    return ['ok' => true, 'virtual' => $from_virtual, 'main' => $from_main, 'total' => $amount, 'short' => 0.0];
}

/* Withdrawals may only touch the main wallet */
function wallet_withdraw($db, $user_id, $amount, $opts = []) {
    return wallet_spend($db, $user_id, $amount,
        array_merge(['category' => 'withdrawal', 'description' => 'Withdrawal'], $opts, ['allow_virtual' => false]));
}

/* Human sentence describing how a purchase was split */
function wallet_split_note($res) {
    if (empty($res['ok'])) return '';
    if ($res['virtual'] > 0 && $res['main'] > 0) {
        return '₹' . number_format($res['virtual'], 2) . ' from Virtual Wallet + ₹' . number_format($res['main'], 2) . ' from Main Wallet';
    }
    if ($res['virtual'] > 0) return '₹' . number_format($res['virtual'], 2) . ' from Virtual Wallet';
    return '₹' . number_format($res['main'], 2) . ' from Main Wallet';
}

/* ---------------------------------------------------------------------------
   History / reporting
   --------------------------------------------------------------------------- */

/* Category → label + colour + icon, used by every history screen */
function wallet_categories() {
    return [
        /* ---- Earning sources shown in the Earning Report ---- */
        'ref_sub_l1'   => ['Refer Subscription — Level 1', 'amber',   'fa-user-plus'],
        'ref_sub_l2'   => ['Refer Subscription — Level 2', 'orange',  'fa-users'],
        'ref_sub_l3'   => ['Refer Subscription — Level 3', 'yellow',  'fa-users-rays'],
        'ref_sale'     => ['Refer Service & Product Sale', 'violet',  'fa-share-nodes'],
        'own_discount' => ['My Discount Saved',            'emerald', 'fa-tags'],
        'sales_earning'=> ['My Sales Earning',             'blue',    'fa-store'],

        /* ---- Everything else ---- */
        'deposit'      => ['Deposit',           'emerald', 'fa-arrow-down'],
        'withdrawal'   => ['Withdrawal',        'rose',    'fa-arrow-up'],
        'purchase'     => ['Purchase',          'indigo',  'fa-cart-shopping'],
        'referral'     => ['Referral Earning',  'amber',   'fa-user-plus'],
        'seller_share' => ['My Sales Earning',  'blue',    'fa-store'],
        'staff_share'  => ['Staff Share',       'violet',  'fa-user-tie'],
        'subscription' => ['Subscription',      'fuchsia', 'fa-crown'],
        'refund'       => ['Refund',            'teal',    'fa-rotate-left'],
        'admin'        => ['Admin Adjustment',  'slate',   'fa-user-shield'],
        'bonus'        => ['Bonus',             'lime',    'fa-gift'],
        'other'        => ['Other',             'gray',    'fa-circle'],
    ];
}

/* Chart / bar colours per earning source */
function wallet_cat_hex($key) {
    $m = [
        'ref_sub_l1'    => '#f59e0b', 'ref_sub_l2' => '#fb923c', 'ref_sub_l3' => '#fbbf24',
        'ref_sale'      => '#8b5cf6', 'own_discount' => '#10b981', 'sales_earning' => '#3b82f6',
        'referral'      => '#f59e0b', 'seller_share' => '#3b82f6', 'staff_share' => '#a78bfa',
        'bonus'         => '#84cc16',
    ];
    return $m[$key] ?? '#94a3b8';
}

function wallet_cat_label($key) {
    $m = wallet_categories();
    return $m[$key][0] ?? ucfirst(str_replace('_', ' ', (string)$key));
}
function wallet_cat_colour($key) {
    $m = wallet_categories();
    return $m[$key][1] ?? 'gray';
}
function wallet_cat_icon($key) {
    $m = wallet_categories();
    return $m[$key][2] ?? 'fa-circle';
}

/**
 * Ledger rows for one user, with optional filters.
 * $f = ['wallet'=>'main|virtual', 'direction'=>'credit|debit', 'category'=>'', 'from'=>'Y-m-d', 'to'=>'Y-m-d', 'limit'=>int]
 */
function wallet_history($db, $user_id, $f = []) {
    wallet_ensure_schema($db);
    $sql = "SELECT * FROM wallet_ledger WHERE user_id = ?";
    $p   = [(int)$user_id];

    // 'report' rows are statistics (e.g. discount saved) — never real transactions
    if (empty($f['include_report'])) { $sql .= " AND wallet <> 'report'"; }

    if (!empty($f['wallet']) && in_array($f['wallet'], ['main','virtual'], true)) { $sql .= " AND wallet = ?"; $p[] = $f['wallet']; }
    if (!empty($f['direction']) && in_array($f['direction'], ['credit','debit'], true)) { $sql .= " AND direction = ?"; $p[] = $f['direction']; }
    if (!empty($f['category'])) { $sql .= " AND category = ?"; $p[] = $f['category']; }
    if (!empty($f['from']))     { $sql .= " AND created_at >= ?"; $p[] = $f['from'] . ' 00:00:00'; }
    if (!empty($f['to']))       { $sql .= " AND created_at <= ?"; $p[] = $f['to'] . ' 23:59:59'; }

    $limit = isset($f['limit']) ? max(1, min(5000, (int)$f['limit'])) : 500;
    $sql .= " ORDER BY created_at DESC, id DESC LIMIT $limit";

    try { return $db->select($sql, $p); } catch (\Throwable $e) { return []; }
}

/* Totals per wallet + direction for one user (optionally date-bounded) */
function wallet_summary($db, $user_id, $from = '', $to = '') {
    wallet_ensure_schema($db);
    $sql = "SELECT wallet, direction, SUM(amount) AS total, COUNT(*) AS cnt FROM wallet_ledger WHERE user_id = ? AND wallet <> 'report'";
    $p = [(int)$user_id];
    if ($from !== '') { $sql .= " AND created_at >= ?"; $p[] = $from . ' 00:00:00'; }
    if ($to   !== '') { $sql .= " AND created_at <= ?"; $p[] = $to . ' 23:59:59'; }
    $sql .= " GROUP BY wallet, direction";

    $out = [
        'main_in' => 0.0, 'main_out' => 0.0, 'virtual_in' => 0.0, 'virtual_out' => 0.0, 'count' => 0,
    ];
    try {
        foreach ($db->select($sql, $p) as $r) {
            $k = $r['wallet'] . '_' . ($r['direction'] === 'credit' ? 'in' : 'out');
            if (isset($out[$k])) $out[$k] = (float)$r['total'];
            $out['count'] += (int)$r['cnt'];
        }
    } catch (\Throwable $e) {}
    return $out;
}

/* ---------------------------------------------------------------------------
   EARNING REPORT — only the categories that are actually income
   --------------------------------------------------------------------------- */
/**
 * The sources shown in the Earning Report.
 * `own_discount` is REPORT ONLY — it is money the user saved, never credited
 * to a wallet, so it is deliberately excluded from every money total.
 *
 * Pass $user_type to tailor the list to whoever is logged in:
 *   customer/member → referral sources + discount
 *   seller          → the same, plus their own sales earning
 *   admin/staff     → the same, plus staff share
 */
function wallet_earning_categories($user_type = '') {
    $base = ['ref_sub_l1', 'ref_sub_l2', 'ref_sub_l3', 'ref_sale', 'own_discount'];
    if ($user_type === 'seller') { $base[] = 'sales_earning'; }
    if ($user_type === 'admin')  { $base[] = 'staff_share'; }
    return $base;
}

/* Categories that represent real money credited to a wallet */
function wallet_money_earning_categories($user_type = '') {
    return array_values(array_filter(wallet_earning_categories($user_type), function ($c) {
        return $c !== 'own_discount';
    }));
}

/* Is this source report-only (no money moved)? */
function wallet_is_report_only($cat) {
    return $cat === 'own_discount';
}

/* Legacy category → new category, so old rows still appear under the right source */
function wallet_category_alias($cat) {
    $map = [
        'referral'     => 'ref_sale',
        'seller_share' => 'sales_earning',
    ];
    return $map[$cat] ?? $cat;
}

/* Expand a new category to itself + any legacy names, for SQL IN() */
function wallet_category_variants($cat) {
    $rev = [
        'ref_sale'      => ['ref_sale', 'referral'],
        'sales_earning' => ['sales_earning', 'seller_share'],
    ];
    return $rev[$cat] ?? [$cat];
}

/* Build a safe SQL IN() list from a set of categories, legacy names included */
function wallet_cats_sql($cats) {
    $out = [];
    foreach ($cats as $c) { foreach (wallet_category_variants($c) as $v) { $out[$v] = true; } }
    $safe = array_map(function ($c) { return preg_replace('/[^a-z0-9_]/', '', $c); }, array_keys($out));
    return "'" . implode("','", $safe) . "'";
}

/* Totals per earning source (own_discount is report-only) */
function earning_by_source($db, $user_id, $from = '', $to = '', $user_type = '') {
    wallet_ensure_schema($db);
    $cats = wallet_earning_categories($user_type);
    $in   = wallet_cats_sql($cats);
    $sql  = "SELECT category, SUM(amount) AS total, COUNT(*) AS cnt
             FROM wallet_ledger
             WHERE user_id = ? AND direction = 'credit' AND category IN ($in)";
    $p = [(int)$user_id];
    if ($from !== '') { $sql .= " AND created_at >= ?"; $p[] = $from . ' 00:00:00'; }
    if ($to   !== '') { $sql .= " AND created_at <= ?"; $p[] = $to . ' 23:59:59'; }
    $sql .= " GROUP BY category";

    $out = [];
    foreach ($cats as $c) { $out[$c] = ['total' => 0.0, 'count' => 0, 'report_only' => wallet_is_report_only($c)]; }
    try {
        foreach ($db->select($sql, $p) as $r) {
            $k = wallet_category_alias($r['category']);        // fold legacy names in
            if (!isset($out[$k])) continue;
            $out[$k]['total'] += (float)$r['total'];
            $out[$k]['count'] += (int)$r['cnt'];
        }
    } catch (\Throwable $e) {}
    return $out;
}

/* Headline numbers — real money only, discount excluded */
function earning_totals($db, $user_id, $user_type = '') {
    wallet_ensure_schema($db);
    $cats = wallet_cats_sql(wallet_money_earning_categories($user_type));
    $base = "SELECT COALESCE(SUM(amount),0) AS t FROM wallet_ledger
             WHERE user_id = ? AND direction = 'credit' AND category IN ($cats)";
    $get = function ($extra, $p) use ($db, $base) {
        try { $r = $db->select($base . $extra, $p); return !empty($r) ? (float)$r[0]['t'] : 0.0; }
        catch (\Throwable $e) { return 0.0; }
    };
    $out = [
        'all_time' => $get('', [(int)$user_id]),
        'month'    => $get(" AND created_at >= ?", [(int)$user_id, date('Y-m-01') . ' 00:00:00']),
        'today'    => $get(" AND created_at >= ?", [(int)$user_id, date('Y-m-d') . ' 00:00:00']),
        'week'     => $get(" AND created_at >= ?", [(int)$user_id, date('Y-m-d', strtotime('-6 days')) . ' 00:00:00']),
    ];

    // Discount saved — shown separately, never added to the earning totals
    try {
        $r = $db->select("SELECT COALESCE(SUM(amount),0) t FROM wallet_ledger
                          WHERE user_id = ? AND direction = 'credit' AND category = 'own_discount'", [(int)$user_id]);
        $out['discount'] = !empty($r) ? (float)$r[0]['t'] : 0.0;
    } catch (\Throwable $e) { $out['discount'] = 0.0; }

    return $out;
}

/* Last N months of real earnings, oldest first — feeds the chart */
function earning_monthly($db, $user_id, $months = 12, $user_type = '') {
    wallet_ensure_schema($db);
    $months = max(1, min(24, (int)$months));
    $cats   = wallet_cats_sql(wallet_money_earning_categories($user_type));
    $start  = date('Y-m-01', strtotime('-' . ($months - 1) . ' months'));

    $map = [];
    try {
        $rows = $db->select(
            "SELECT DATE_FORMAT(created_at, '%Y-%m') AS ym, SUM(amount) AS total
             FROM wallet_ledger
             WHERE user_id = ? AND direction = 'credit' AND category IN ($cats) AND created_at >= ?
             GROUP BY ym", [(int)$user_id, $start . ' 00:00:00']);
        foreach ($rows as $r) { $map[$r['ym']] = (float)$r['total']; }
    } catch (\Throwable $e) {}

    $out = [];
    for ($i = $months - 1; $i >= 0; $i--) {
        $ts = strtotime("-$i months");
        $ym = date('Y-m', $ts);
        $out[] = ['label' => date('M y', $ts), 'ym' => $ym, 'total' => $map[$ym] ?? 0.0];
    }
    return $out;
}

/* Every earning row, for the table and the CSV export */
function earning_rows($db, $user_id, $from = '', $to = '', $category = '', $limit = 1000, $user_type = '') {
    wallet_ensure_schema($db);
    $cats = wallet_earning_categories($user_type);
    if ($category !== '' && in_array($category, $cats, true)) { $cats = [$category]; }
    $in  = wallet_cats_sql($cats);

    $sql = "SELECT * FROM wallet_ledger
            WHERE user_id = ? AND direction = 'credit' AND category IN ($in)";
    $p = [(int)$user_id];
    if ($from !== '') { $sql .= " AND created_at >= ?"; $p[] = $from . ' 00:00:00'; }
    if ($to   !== '') { $sql .= " AND created_at <= ?"; $p[] = $to . ' 23:59:59'; }
    $sql .= " ORDER BY created_at DESC, id DESC LIMIT " . max(1, min(5000, (int)$limit));

    try { return $db->select($sql, $p); } catch (\Throwable $e) { return []; }
}

/* ---------------------------------------------------------------------------
   BACKFILL — turn old `transactions` rows into ledger rows exactly once,
   so existing users don't open an empty history.
   --------------------------------------------------------------------------- */
function wallet_backfill_from_transactions($db) {
    wallet_ensure_schema($db);
    try {
        $c = $db->select("SELECT COUNT(*) AS n FROM wallet_ledger");
        if (!empty($c) && (int)$c[0]['n'] > 0) return 0;   // already has data — never run twice
    } catch (\Throwable $e) { return 0; }

    $rows = [];
    try { $rows = $db->select("SELECT * FROM transactions ORDER BY id ASC"); }
    catch (\Throwable $e) { return 0; }

    $n = 0;
    foreach ($rows as $t) {
        $d   = strtolower((string)$t['description']);
        $cat = 'other';
        if (strpos($d, 'referral') !== false)                                   $cat = 'referral';
        elseif (strpos($d, 'seller share') !== false)                           $cat = 'seller_share';
        elseif (strpos($d, 'staff') !== false)                                  $cat = 'staff_share';
        elseif (strpos($d, 'subscription') !== false || strpos($d, 'premium') !== false) $cat = 'subscription';
        elseif (strpos($d, 'purchas') !== false)                                $cat = 'purchase';
        elseif (strpos($d, 'refund') !== false)                                 $cat = 'refund';
        elseif (strpos($d, 'withdraw') !== false)                               $cat = 'withdrawal';
        elseif (strpos($d, 'recharge') !== false || strpos($d, 'add money') !== false || strpos($d, 'razorpay') !== false) $cat = 'deposit';
        elseif (strpos($d, 'admin') !== false)                                  $cat = 'admin';

        try {
            $db->insert("INSERT INTO wallet_ledger
                (user_id, wallet, direction, amount, balance_after, category, description, reference_type, reference_id, created_at)
                VALUES (?, 'main', ?, ?, 0, ?, ?, 'transaction', ?, ?)", [
                (int)$t['user_id'], $t['type'], $t['amount'], $cat,
                $t['description'], (int)$t['id'], $t['created_at'],
            ]);
            $n++;
        } catch (\Throwable $e) {}
    }
    return $n;
}
