<?php
/* ============================================================================
   WBMCZ — Wallet to Bank

   Flow
     1. User opens Wallet → Wallet to Bank.
        - Bank details already in KYC  → the form is pre-filled from kyc_details
        - Nothing on file              → a popup form opens, every field required
     2. Submitting creates a `bank_accounts` row with status = pending.
     3. Admin → Wallet Management → Bank Request Pending → Approve / Reject.
     4. Once approved, withdrawal unlocks: pick the bank, enter an amount,
        submit → the money leaves the MAIN wallet immediately and a
        `withdrawal_requests` row is created with status = pending.
     5. Admin → Transaction Update → Approve (with a UTR / reference) or Reject.
        Rejecting refunds the full amount back to the user's main wallet.

   Only the MAIN wallet can be withdrawn — the virtual (referral) wallet never.
   ============================================================================ */

require_once __DIR__ . '/wallet.php';

if (!defined('BANK_PENDING'))  define('BANK_PENDING', 0);
if (!defined('BANK_APPROVED')) define('BANK_APPROVED', 1);
if (!defined('BANK_REJECTED')) define('BANK_REJECTED', 2);

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

    try {
        $conn->query("CREATE TABLE IF NOT EXISTS bank_accounts (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            holder_name VARCHAR(150) NOT NULL,
            bank_name VARCHAR(150) NOT NULL,
            branch_name VARCHAR(150) NOT NULL,
            account_number VARCHAR(50) NOT NULL,
            confirm_account VARCHAR(50) NULL,
            ifsc_code VARCHAR(30) NOT NULL,
            account_type VARCHAR(20) NOT NULL DEFAULT 'savings',
            mobile VARCHAR(20) NULL,
            upi_id VARCHAR(120) NULL,
            passbook VARCHAR(255) NULL,
            status TINYINT(1) NOT NULL DEFAULT 0,
            admin_remark TEXT NULL,
            reviewed_by INT NULL,
            reviewed_by_name VARCHAR(120) NULL,
            reviewed_at DATETIME NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NULL,
            UNIQUE KEY uniq_bank_user (user_id),
            KEY idx_bank_status (status)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    } catch (\Throwable $e) { /* ignore */ }

    try {
        $conn->query("CREATE TABLE IF NOT EXISTS withdrawal_requests (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            bank_account_id INT NULL,
            reference_no VARCHAR(40) NOT NULL,
            amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
            charge DECIMAL(10,2) NOT NULL DEFAULT 0.00,
            net_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
            bank_name VARCHAR(150) NULL,
            account_number VARCHAR(50) NULL,
            ifsc_code VARCHAR(30) NULL,
            holder_name VARCHAR(150) NULL,
            status VARCHAR(20) NOT NULL DEFAULT 'pending',
            utr_number VARCHAR(60) NULL,
            admin_remark TEXT NULL,
            reviewed_by INT NULL,
            reviewed_by_name VARCHAR(120) NULL,
            reviewed_at DATETIME NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            UNIQUE KEY uniq_wd_ref (reference_no),
            KEY idx_wd_user (user_id, created_at),
            KEY idx_wd_status (status)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    } catch (\Throwable $e) { /* ignore */ }

    // Default settings, inserted only if missing
    foreach (bank_default_settings() as $k => $v) {
        try {
            $r = $db->select("SELECT id FROM settings WHERE setting_key = ?", [$k]);
            if (empty($r)) {
                $db->insert("INSERT INTO settings (setting_key, setting_value, setting_type) VALUES (?, ?, 'text')", [$k, $v]);
            }
        } catch (\Throwable $e) { /* ignore */ }
    }
}

function bank_default_settings() {
    return [
        'withdraw_enabled'      => '1',
        'withdraw_min'          => '100',
        'withdraw_max'          => '50000',
        'withdraw_charge_type'  => 'none',   // none | percent | fixed
        'withdraw_charge_value' => '0',
        'withdraw_note'         => 'Withdrawals are processed within 24–48 working hours.',
    ];
}

/* ---------------------------------------------------------------------------
   Settings
   --------------------------------------------------------------------------- */
function bank_settings($db) {
    $out = bank_default_settings();
    try {
        $keys = "'" . implode("','", array_keys($out)) . "'";
        foreach ($db->select("SELECT setting_key, setting_value FROM settings WHERE setting_key IN ($keys)") as $r) {
            $out[$r['setting_key']] = $r['setting_value'];
        }
    } catch (\Throwable $e) {}
    return $out;
}

function bank_save_setting($db, $key, $value) {
    if (!array_key_exists($key, bank_default_settings())) return false;
    try {
        $r = $db->select("SELECT id FROM settings WHERE setting_key = ?", [$key]);
        if (!empty($r)) { $db->insert("UPDATE settings SET setting_value = ? WHERE setting_key = ?", [$value, $key]); }
        else            { $db->insert("INSERT INTO settings (setting_key, setting_value, setting_type) VALUES (?, ?, 'text')", [$key, $value]); }
        return true;
    } catch (\Throwable $e) { return false; }
}

/* Charge on a given amount, per the admin settings */
function bank_charge_for($settings, $amount) {
    $amount = (float)$amount;
    $type   = $settings['withdraw_charge_type'] ?? 'none';
    $val    = (float)($settings['withdraw_charge_value'] ?? 0);
    if ($type === 'percent') return round(min($amount, ($amount * $val) / 100), 2);
    if ($type === 'fixed')   return round(min($amount, $val), 2);
    return 0.00;
}

function bank_charge_label($settings) {
    $type = $settings['withdraw_charge_type'] ?? 'none';
    $val  = (float)($settings['withdraw_charge_value'] ?? 0);
    if ($type === 'percent') return rtrim(rtrim(number_format($val, 2), '0'), '.') . '%';
    if ($type === 'fixed')   return '₹' . number_format($val, 2);
    return 'No charge';
}

/* ---------------------------------------------------------------------------
   Bank account
   --------------------------------------------------------------------------- */
function bank_get_account($db, $user_id) {
    bank_ensure_schema($db);
    try {
        $r = $db->select("SELECT * FROM bank_accounts WHERE user_id = ? LIMIT 1", [(int)$user_id]);
        return !empty($r) ? $r[0] : null;
    } catch (\Throwable $e) { return null; }
}

/**
 * Pre-fill values for the bank form.
 * Uses the saved bank account if there is one, otherwise the KYC bank section.
 */
function bank_prefill($db, $user_id) {
    $acc = bank_get_account($db, $user_id);
    if ($acc) {
        return [
            'holder_name'    => $acc['holder_name'],
            'bank_name'      => $acc['bank_name'],
            'branch_name'    => $acc['branch_name'],
            'account_number' => $acc['account_number'],
            'ifsc_code'      => $acc['ifsc_code'],
            'account_type'   => $acc['account_type'],
            'mobile'         => $acc['mobile'],
            'upi_id'         => $acc['upi_id'],
            'source'         => 'bank',
        ];
    }

    $k = [];
    try {
        $r = $db->select("SELECT full_name, bank_name, branch_name, account_number, ifsc_code FROM kyc_details WHERE user_id = ?", [(int)$user_id]);
        if (!empty($r)) $k = $r[0];
    } catch (\Throwable $e) {}

    $u = [];
    try {
        $r = $db->select("SELECT name, mobile FROM users WHERE id = ?", [(int)$user_id]);
        if (!empty($r)) $u = $r[0];
    } catch (\Throwable $e) {}

    $has_kyc_bank = !empty($k['account_number']) && !empty($k['ifsc_code']);

    return [
        'holder_name'    => $k['full_name'] ?? ($u['name'] ?? ''),
        'bank_name'      => $k['bank_name'] ?? '',
        'branch_name'    => $k['branch_name'] ?? '',
        'account_number' => $k['account_number'] ?? '',
        'ifsc_code'      => $k['ifsc_code'] ?? '',
        'account_type'   => 'savings',
        'mobile'         => $u['mobile'] ?? '',
        'upi_id'         => '',
        'source'         => $has_kyc_bank ? 'kyc' : 'empty',
    ];
}

/**
 * Create or update the user's bank account. Always lands back on "pending".
 * Returns ['ok'=>bool, 'error'=>string]
 */
function bank_submit_account($db, $user_id, $in, $passbook = null) {
    bank_ensure_schema($db);

    $f = [
        'holder_name'    => trim((string)($in['holder_name'] ?? '')),
        'bank_name'      => trim((string)($in['bank_name'] ?? '')),
        'branch_name'    => trim((string)($in['branch_name'] ?? '')),
        'account_number' => preg_replace('/\s+/', '', (string)($in['account_number'] ?? '')),
        'confirm_account'=> preg_replace('/\s+/', '', (string)($in['confirm_account'] ?? '')),
        'ifsc_code'      => strtoupper(preg_replace('/\s+/', '', (string)($in['ifsc_code'] ?? ''))),
        'account_type'   => in_array(($in['account_type'] ?? ''), ['savings','current'], true) ? $in['account_type'] : 'savings',
        'mobile'         => preg_replace('/\D/', '', (string)($in['mobile'] ?? '')),
        'upi_id'         => trim((string)($in['upi_id'] ?? '')),
    ];

    // Every field except UPI is mandatory
    foreach (['holder_name' => 'Account holder name', 'bank_name' => 'Bank name', 'branch_name' => 'Branch name',
              'account_number' => 'Account number', 'ifsc_code' => 'IFSC code', 'mobile' => 'Mobile number'] as $k => $label) {
        if ($f[$k] === '') return ['ok' => false, 'error' => "$label is required."];
    }
    if (!preg_match('/^\d{9,18}$/', $f['account_number'])) {
        return ['ok' => false, 'error' => 'Account number must be 9 to 18 digits.'];
    }
    if ($f['confirm_account'] !== '' && $f['confirm_account'] !== $f['account_number']) {
        return ['ok' => false, 'error' => 'The two account numbers do not match.'];
    }
    if (!preg_match('/^[A-Z]{4}0[A-Z0-9]{6}$/', $f['ifsc_code'])) {
        return ['ok' => false, 'error' => 'IFSC code looks invalid (example: SBIN0001234).'];
    }
    if (!preg_match('/^\d{10}$/', $f['mobile'])) {
        return ['ok' => false, 'error' => 'Mobile number must be 10 digits.'];
    }

    $existing = bank_get_account($db, $user_id);
    if ($existing && (int)$existing['status'] === BANK_APPROVED && empty($in['allow_edit_approved'])) {
        return ['ok' => false, 'error' => 'Your bank account is already approved. Contact support to change it.'];
    }

    $file = $existing['passbook'] ?? null;
    if ($passbook !== null && $passbook !== '') $file = $passbook;

    try {
        if ($existing) {
            $db->insert("UPDATE bank_accounts SET holder_name=?, bank_name=?, branch_name=?, account_number=?, confirm_account=?,
                         ifsc_code=?, account_type=?, mobile=?, upi_id=?, passbook=?, status=?, admin_remark=NULL, updated_at=NOW()
                         WHERE user_id=?",
                [$f['holder_name'], $f['bank_name'], $f['branch_name'], $f['account_number'], $f['confirm_account'],
                 $f['ifsc_code'], $f['account_type'], $f['mobile'], $f['upi_id'], $file, BANK_PENDING, (int)$user_id]);
        } else {
            $db->insert("INSERT INTO bank_accounts (user_id, holder_name, bank_name, branch_name, account_number, confirm_account,
                         ifsc_code, account_type, mobile, upi_id, passbook, status)
                         VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
                [(int)$user_id, $f['holder_name'], $f['bank_name'], $f['branch_name'], $f['account_number'], $f['confirm_account'],
                 $f['ifsc_code'], $f['account_type'], $f['mobile'], $f['upi_id'], $file, BANK_PENDING]);
        }
    } catch (\Throwable $e) {
        return ['ok' => false, 'error' => 'Could not save the bank details. Please try again.'];
    }

    return ['ok' => true, 'error' => ''];
}

/* Admin: approve / reject a bank account */
function bank_review_account($db, $bank_id, $approve, $remark, $me) {
    bank_ensure_schema($db);
    $status = $approve ? BANK_APPROVED : BANK_REJECTED;
    try {
        $db->insert("UPDATE bank_accounts SET status=?, admin_remark=?, reviewed_by=?, reviewed_by_name=?, reviewed_at=NOW() WHERE id=?",
            [$status, $remark, (int)$me['id'], $me['name'], (int)$bank_id]);

        $r = $db->select("SELECT user_id, bank_name FROM bank_accounts WHERE id = ?", [(int)$bank_id]);
        if (!empty($r)) {
            $msg = $approve
                ? 'Your bank account (' . $r[0]['bank_name'] . ') has been verified. Wallet to Bank is now active.'
                : 'Your bank account request was rejected.' . ($remark !== '' ? ' Reason: ' . $remark : '');
            $db->insert("INSERT INTO notifications (user_id, title, message, type, link, is_read) VALUES (?, ?, ?, 'withdrawal', 'wallet.php#to_bank', 0)",
                [(int)$r[0]['user_id'], $approve ? 'Bank account verified' : 'Bank account rejected', $msg]);
        }
        return true;
    } catch (\Throwable $e) { return false; }
}

/* ---------------------------------------------------------------------------
   Withdrawal
   --------------------------------------------------------------------------- */
function bank_reference_no() {
    return 'WD' . date('ymd') . strtoupper(substr(bin2hex(random_bytes(3)), 0, 6));
}

/**
 * Create a withdrawal request. Money leaves the MAIN wallet immediately.
 * Returns ['ok'=>bool, 'error'=>string, 'ref'=>string]
 */
function bank_request_withdrawal($db, $user_id, $amount) {
    bank_ensure_schema($db);
    $s = bank_settings($db);

    if (($s['withdraw_enabled'] ?? '1') !== '1') {
        return ['ok' => false, 'error' => 'Withdrawals are temporarily disabled. Please try again later.'];
    }

    $acc = bank_get_account($db, $user_id);
    if (!$acc || (int)$acc['status'] !== BANK_APPROVED) {
        return ['ok' => false, 'error' => 'Your bank account must be verified by admin first.'];
    }

    $amount = round((float)$amount, 2);
    $min = (float)($s['withdraw_min'] ?? 100);
    $max = (float)($s['withdraw_max'] ?? 0);

    if ($amount <= 0)               return ['ok' => false, 'error' => 'Enter a valid amount.'];
    if ($amount < $min)             return ['ok' => false, 'error' => 'Minimum withdrawal is ₹' . number_format($min, 2) . '.'];
    if ($max > 0 && $amount > $max) return ['ok' => false, 'error' => 'Maximum withdrawal is ₹' . number_format($max, 2) . ' per request.'];

    // Only the MAIN wallet is withdrawable
    $bal = wallet_balances($db, $user_id);
    if ($bal['main'] + 0.001 < $amount) {
        return ['ok' => false, 'error' => 'Your main wallet has only ₹' . number_format($bal['main'], 2) .
                                          '. The virtual wallet cannot be withdrawn.'];
    }

    // Block a second request while one is still pending
    try {
        $p = $db->select("SELECT id FROM withdrawal_requests WHERE user_id = ? AND status = 'pending' LIMIT 1", [(int)$user_id]);
        if (!empty($p)) return ['ok' => false, 'error' => 'You already have a withdrawal awaiting approval.'];
    } catch (\Throwable $e) {}

    $charge = bank_charge_for($s, $amount);
    $net    = round($amount - $charge, 2);
    $ref    = bank_reference_no();

    $wid = $db->insert("INSERT INTO withdrawal_requests
        (user_id, bank_account_id, reference_no, amount, charge, net_amount, bank_name, account_number, ifsc_code, holder_name, status)
        VALUES (?,?,?,?,?,?,?,?,?,?,'pending')",
        [(int)$user_id, (int)$acc['id'], $ref, $amount, $charge, $net,
         $acc['bank_name'], $acc['account_number'], $acc['ifsc_code'], $acc['holder_name']]);

    // Take the money out of the main wallet now (virtual is never touched)
    $pay = wallet_spend($db, $user_id, $amount, [
        'category'       => 'withdrawal',
        'description'    => 'Withdrawal to ' . $acc['bank_name'] . ' ****' . substr($acc['account_number'], -4) . " ($ref)",
        'reference_type' => 'withdrawal',
        'reference_id'   => $wid,
        'allow_virtual'  => false,
    ]);

    if (!$pay['ok']) {
        try { $db->insert("DELETE FROM withdrawal_requests WHERE id = ?", [$wid]); } catch (\Throwable $e) {}
        return ['ok' => false, 'error' => 'Insufficient main wallet balance — ₹' . number_format($pay['short'], 2) . ' short.'];
    }

    return ['ok' => true, 'error' => '', 'ref' => $ref];
}

/**
 * Admin: approve (money actually sent) or reject (refund to the wallet).
 */
function bank_review_withdrawal($db, $wid, $action, $utr, $remark, $me) {
    bank_ensure_schema($db);
    $wid = (int)$wid;

    try {
        $r = $db->select("SELECT * FROM withdrawal_requests WHERE id = ?", [$wid]);
        if (empty($r)) return ['ok' => false, 'error' => 'Request not found.'];
        $w = $r[0];
    } catch (\Throwable $e) { return ['ok' => false, 'error' => 'Request not found.']; }

    if ($w['status'] !== 'pending' && $w['status'] !== 'processing') {
        return ['ok' => false, 'error' => 'This request has already been ' . $w['status'] . '.'];
    }

    if ($action === 'approve') {
        $db->insert("UPDATE withdrawal_requests SET status='approved', utr_number=?, admin_remark=?, reviewed_by=?, reviewed_by_name=?, reviewed_at=NOW() WHERE id=?",
            [$utr, $remark, (int)$me['id'], $me['name'], $wid]);
        try {
            $db->insert("INSERT INTO notifications (user_id, title, message, type, link, is_read) VALUES (?, ?, ?, 'withdrawal', 'wallet.php#to_bank', 0)", [
                (int)$w['user_id'], 'Withdrawal sent successfully',
                '₹' . number_format((float)$w['net_amount'], 2) . ' has been sent to your ' . $w['bank_name'] .
                ' account (' . $w['reference_no'] . ')' . ($utr !== '' ? '. UTR: ' . $utr : '.'),
            ]);
        } catch (\Throwable $e) {}
        return ['ok' => true, 'error' => ''];
    }

    if ($action === 'processing') {
        $db->insert("UPDATE withdrawal_requests SET status='processing', admin_remark=?, reviewed_by=?, reviewed_by_name=?, reviewed_at=NOW() WHERE id=?",
            [$remark, (int)$me['id'], $me['name'], $wid]);
        return ['ok' => true, 'error' => ''];
    }

    // Reject — put the full amount back into the main wallet
    $db->insert("UPDATE withdrawal_requests SET status='rejected', admin_remark=?, reviewed_by=?, reviewed_by_name=?, reviewed_at=NOW() WHERE id=?",
        [$remark, (int)$me['id'], $me['name'], $wid]);

    wallet_credit($db, (int)$w['user_id'], WALLET_MAIN, (float)$w['amount'], [
        'category'       => 'refund',
        'description'    => 'Withdrawal ' . $w['reference_no'] . ' rejected — amount refunded' . ($remark !== '' ? ' (' . $remark . ')' : ''),
        'reference_type' => 'withdrawal',
        'reference_id'   => $wid,
        'created_by'     => (int)$me['id'],
    ]);

    try {
        $db->insert("INSERT INTO notifications (user_id, title, message, type, link, is_read) VALUES (?, ?, ?, 'withdrawal', 'wallet.php#to_bank', 0)", [
            (int)$w['user_id'], 'Withdrawal rejected',
            'Your withdrawal ' . $w['reference_no'] . ' was rejected and ₹' . number_format((float)$w['amount'], 2) .
            ' has been returned to your wallet.' . ($remark !== '' ? ' Reason: ' . $remark : ''),
        ]);
    } catch (\Throwable $e) {}

    return ['ok' => true, 'error' => ''];
}

/* ---------------------------------------------------------------------------
   Reads
   --------------------------------------------------------------------------- */
function bank_withdrawals($db, $user_id, $limit = 100) {
    bank_ensure_schema($db);
    try {
        return $db->select("SELECT * FROM withdrawal_requests WHERE user_id = ? ORDER BY created_at DESC, id DESC LIMIT " . max(1, (int)$limit),
            [(int)$user_id]);
    } catch (\Throwable $e) { return []; }
}

function bank_latest_withdrawal($db, $user_id) {
    $r = bank_withdrawals($db, $user_id, 1);
    return !empty($r) ? $r[0] : null;
}

function bank_withdraw_totals($db, $user_id) {
    bank_ensure_schema($db);
    $out = ['pending' => 0.0, 'approved' => 0.0, 'rejected' => 0.0, 'count' => 0];
    try {
        foreach ($db->select("SELECT status, COALESCE(SUM(amount),0) t, COUNT(*) c FROM withdrawal_requests WHERE user_id = ? GROUP BY status", [(int)$user_id]) as $r) {
            $k = $r['status'] === 'processing' ? 'pending' : $r['status'];
            if (isset($out[$k])) $out[$k] += (float)$r['t'];
            $out['count'] += (int)$r['c'];
        }
    } catch (\Throwable $e) {}
    return $out;
}

/* ---------------------------------------------------------------------------
   Presentation helpers
   --------------------------------------------------------------------------- */
function bank_status_meta($status) {
    switch ($status) {
        case 'approved':   return ['Approved &amp; Sent Successfully', 'emerald', 'fa-circle-check'];
        case 'processing': return ['Processing', 'blue', 'fa-spinner'];
        case 'rejected':   return ['Rejected &amp; Refunded', 'rose', 'fa-circle-xmark'];
    }
    return ['Pending Approval', 'amber', 'fa-clock'];
}

function bank_account_status_meta($status) {
    switch ((int)$status) {
        case BANK_APPROVED: return ['Verified', 'emerald', 'fa-circle-check'];
        case BANK_REJECTED: return ['Rejected', 'rose', 'fa-circle-xmark'];
    }
    return ['Awaiting Verification', 'amber', 'fa-clock'];
}

function bank_mask_account($acc) {
    $acc = (string)$acc;
    if (strlen($acc) <= 4) return $acc;
    return str_repeat('•', max(4, strlen($acc) - 4)) . substr($acc, -4);
}

/* Upload a passbook / cheque image. $rel = 'uploads/bank/' or '../uploads/bank/' */
function bank_handle_upload($field, $dir) {
    if (empty($_FILES[$field]['tmp_name']) || (int)$_FILES[$field]['error'] !== 0) return null;
    if (!is_dir($dir)) { @mkdir($dir, 0777, true); }
    $ext = strtolower(pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION));
    if (!in_array($ext, ['jpg','jpeg','png','webp','pdf'], true)) return null;
    $fn = 'bank_' . time() . '_' . rand(1000, 9999) . '.' . $ext;
    if (move_uploaded_file($_FILES[$field]['tmp_name'], $dir . $fn)) return 'uploads/bank/' . $fn;
    return null;
}
