<?php
/**
 * qxz-fk 主页面：营销工作台 + 管理端（同一入口按角色渲染）
 * 营销：许可证余额、开卡/续费、我的开卡记录
 * 管理：营销列表、授权发放/回收、设/取消营销身份、全部流水
 */
require_once __DIR__ . '/includes/auth.php';
$user = fkRequireUser();
fkValidateCsrf();

$db = fkDB();

// ───────────────────────── API ─────────────────────────
if (isset($_GET['action'])) {
    header('Content-Type: application/json; charset=utf-8');
    $act = $_GET['action'];
    $b = json_decode(file_get_contents('php://input'), true) ?: $_POST;
    $isAdmin = $user['role'] === 'admin';

    // 营销/管理通用：我的信息（含余额）
    if ($act === 'me') {
        echo json_encode(['ok' => true, 'role' => $user['role'], 'name' => $user['username'],
            'balance' => fkBalance($user['id'])]);
        exit;
    }

    // 营销/管理通用：开卡记录（营销=自己的，管理=全部）
    if ($act === 'my_sales' || $act === 'all_sales') {
        if ($act === 'all_sales' && !$isAdmin) { echo json_encode(['error' => '仅管理员']); exit; }
        $limit = min(200, max(1, intval($b['limit'] ?? 50)));
        if ($isAdmin && $act === 'all_sales') {
            $rows = $db->query("SELECT * FROM fk_sales ORDER BY id DESC LIMIT $limit")->fetchAll();
        } else {
            $st = $db->prepare("SELECT * FROM fk_sales WHERE marketer_id = ? ORDER BY id DESC LIMIT $limit");
            $st->execute([$user['id']]);
            $rows = $st->fetchAll();
        }
        echo json_encode(['sales' => $rows]);
        exit;
    }

    // 开卡前查重/续费前查状态：返回用户是否已存在及其档位（营销据此选择开卡或续费）
    if ($act === 'check_user') {
        $name = trim($b['name'] ?? '');
        if (strlen($name) < 3) { echo json_encode(['error' => '用户名需 3-32 位']); exit; }
        $st = $db->prepare("SELECT u.id, u.banned, u.role, gp.tier, gp.pay_type, gp.head_expires, gp.headless_expires, gp.cloud_expires
                            FROM users u LEFT JOIN game_perms gp ON u.id = gp.user_id AND gp.game_code = ?
                            WHERE u.username = ? COLLATE NOCASE");
        $st->execute([FK_GAME_CODE, $name]);
        $u = $st->fetch();
        if (!$u) { echo json_encode(['exists' => false]); exit; }
        $life = FK_PERM_LIFETIME;
        $exps = [(int)$u['head_expires'], (int)$u['headless_expires'], (int)$u['cloud_expires']];
        $isLife = max($exps) >= $life;
        $valid = array_values(array_filter($exps, fn($e) => $e > 0 && $e < $life));
        echo json_encode(['exists' => true, 'banned' => (int)$u['banned'] === 1, 'role' => $u['role'],
            'tier' => (int)$u['tier'], 'pay_type' => $u['pay_type'], 'is_life' => $isLife,
            'expires' => $valid ? max($valid) : 0]);
        exit;
    }

    // 开卡（营销/管理员均可，管理员不扣点）
    if ($act === 'open' && $_SERVER['REQUEST_METHOD'] === 'POST') {
        try {
            $name = trim($b['name'] ?? '');
            $pwd = (string)($b['pwd'] ?? '');
            $tier = intval($b['tier'] ?? 0);
            $months = intval($b['months'] ?? 1);
            $payType = fkIsLife($tier) ? 'life' : fkPayType($months);
            $cost = fkCostOf($tier, $payType);
            if (!$isAdmin && fkBalance($user['id']) < $cost) {
                throw new RuntimeException('授权点数不足（本次需 ' . $cost . ' 点），请联系管理员充值');
            }
            $r = fkOpenAccount($user['username'], $user['id'], $name, $pwd, $tier, $months);
            if ($isAdmin) {   // 管理员开卡不扣点，但同样落流水（cost=0）
                $db->prepare("UPDATE fk_sales SET cost = 0 WHERE marketer_id = ? AND target_name = ? ORDER BY id DESC LIMIT 1")
                   ->execute([$user['id'], trim($b['name'] ?? '')]);
            }
            echo json_encode(['ok' => true, 'uid' => $r['uid'], 'expires' => $r['expires'], 'cost' => $isAdmin ? 0 : $cost,
                'balance' => fkBalance($user['id'])]);
        } catch (Throwable $e) {
            echo json_encode(['error' => $e->getMessage()]);
        }
        exit;
    }

    // 续费（仅营销扣点；管理员续费不扣点）
    if ($act === 'renew' && $_SERVER['REQUEST_METHOD'] === 'POST') {
        try {
            $name = trim($b['name'] ?? '');
            $tier = intval($b['tier'] ?? 0);
            $months = intval($b['months'] ?? 1);
            $cost = fkCostOf($tier, fkPayType($months));
            if (!$isAdmin && fkBalance($user['id']) < $cost) {
                throw new RuntimeException('授权点数不足（本次需 ' . $cost . ' 点），请联系管理员充值');
            }
            $r = fkRenewAccount($user['username'], $user['id'], $name, $tier, $months);
            if ($isAdmin) {
                $db->prepare("UPDATE fk_sales SET cost = 0 WHERE marketer_id = ? AND target_name = ? ORDER BY id DESC LIMIT 1")
                   ->execute([$user['id'], $name]);
            }
            echo json_encode(['ok' => true, 'uid' => $r['uid'], 'expires' => $r['expires'], 'cost' => $isAdmin ? 0 : $cost,
                'balance' => fkBalance($user['id'])]);
        } catch (Throwable $e) {
            echo json_encode(['error' => $e->getMessage()]);
        }
        exit;
    }

    // ══════════ 以下仅管理员 ══════════
    if (!$isAdmin) { echo json_encode(['error' => '仅管理员']); exit; }

    // 营销列表（含余额、累计开卡数）
    if ($act === 'list_marketers') {
        $rows = $db->query("SELECT u.id, u.username, u.banned, u.created_at, u.last_login,
            COALESCE(lc.balance, 0) AS balance,
            (SELECT COUNT(*) FROM fk_sales s WHERE s.marketer_id = u.id AND s.action = 'open') AS opened,
            (SELECT COUNT(*) FROM fk_sales s WHERE s.marketer_id = u.id AND s.action = 'renew') AS renewed
            FROM users u LEFT JOIN license_credit lc ON lc.user_id = u.id
            WHERE u.role = 'marketer' ORDER BY u.id")->fetchAll();
        echo json_encode(['marketers' => $rows]);
        exit;
    }

    // 授权发放/回收（delta 可正可负）
    if ($act === 'grant_credit' && $_SERVER['REQUEST_METHOD'] === 'POST') {
        $uid = intval($b['uid'] ?? 0);
        $delta = intval($b['delta'] ?? 0);
        $note = trim((string)($b['note'] ?? ''));
        if ($delta === 0) { echo json_encode(['error' => '数量不能为 0']); exit; }
        $st = $db->prepare("SELECT username FROM users WHERE id = ? AND role = 'marketer'");
        $st->execute([$uid]);
        $t = $st->fetch();
        if (!$t) { echo json_encode(['error' => '营销账号不存在']); exit; }
        $bal = fkGrantCredit($user['id'], $user['username'], $uid, $t['username'], $delta, $note);   // 负余额已在 DB 层截断到 0
        echo json_encode(['ok' => true, 'balance' => $bal]);
        exit;
    }

    // 设为营销 / 取消营销（用户名操作，针对已存在的用户）
    if ($act === 'set_marketer' && $_SERVER['REQUEST_METHOD'] === 'POST') {
        $name = trim($b['name'] ?? '');
        $on = intval($b['on'] ?? 1) === 1;
        $st = $db->prepare("SELECT id, role FROM users WHERE username = ? COLLATE NOCASE");
        $st->execute([$name]);
        $t = $st->fetch();
        if (!$t) { echo json_encode(['error' => '用户不存在（只能将已有用户设为营销）']); exit; }
        if ($t['role'] === 'admin') { echo json_encode(['error' => '不能修改管理员角色']); exit; }
        $db->prepare("UPDATE users SET role = ? WHERE id = ?")->execute([$on ? 'marketer' : 'user', $t['id']]);
        if ($on) {   // 首次设为营销时初始化余额行（0 点）
            $db->prepare("INSERT INTO license_credit (user_id, balance) VALUES (?, 0) ON CONFLICT(user_id) DO NOTHING")
               ->execute([$t['id']]);
        }
        echo json_encode(['ok' => true]);
        exit;
    }

    // 授权发放流水
    if ($act === 'grant_log') {
        $rows = $db->query("SELECT * FROM license_grant ORDER BY id DESC LIMIT 100")->fetchAll();
        echo json_encode(['logs' => $rows]);
        exit;
    }

    echo json_encode(['error' => 'unknown action']);
    exit;
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars(FK_SITE_NAME) ?></title>
<style>
*{box-sizing:border-box}
body{margin:0;font-family:'Segoe UI',sans-serif;background:#0f1420;color:#dde3ef;font-size:13px}
header{background:#161d30;border-bottom:1px solid #2c3a55;padding:10px 16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
header .title{color:#7aa2ff;font-weight:bold;font-size:15px}
.muted{color:#8a97ad}
.btn{padding:6px 16px;border:0;border-radius:5px;cursor:pointer;font-size:13px}
.btn.green{background:#1d7a45;color:#fff}.btn.red{background:#a13a3a;color:#fff}
.btn.gray{background:#2c3a55;color:#cfd8ea}.btn.blue{background:#2b579a;color:#fff}
.btn.sm{font-size:11px;padding:2px 8px}
nav{background:#131a2b;border-bottom:1px solid #2c3a55;display:flex;flex-wrap:wrap}
nav button{background:none;border:0;color:#aeb9cf;padding:9px 16px;cursor:pointer;font-size:13px;border-bottom:2px solid transparent}
nav button.on{color:#7aa2ff;border-bottom-color:#7aa2ff}
main{padding:14px 18px}
.tab{display:none}.tab.on{display:block}
table{border-collapse:collapse;width:100%}
th,td{border:1px solid #2c3a55;padding:5px 8px;text-align:left}
th{background:#1c2540;color:#9fb3da}
tr:nth-child(even){background:#141b2e}
.badge{background:#2b579a;color:#fff;border-radius:10px;padding:1px 8px;font-size:11px}
.badge.green{background:#1d7a45}.badge.red{background:#a13a3a}.badge.orange{background:#b9770e}
.card{background:#161d30;border:1px solid #2c3a55;border-radius:8px;padding:12px;margin-bottom:12px}
h3{margin:0 0 10px;color:#7aa2ff;font-size:14px}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:8px}
label.fk{display:block;color:#8a97ad;font-size:12px;margin:8px 0 4px}
input,select{background:#101626;border:1px solid #2c3a55;color:#e0e6f0;padding:6px 8px;border-radius:4px;font-size:13px}
.balance{font-size:30px;font-weight:bold;color:#2ecc71}
</style>
</head>
<body>
<header>
  <span class="title"><?= htmlspecialchars(FK_SITE_NAME) ?></span>
  <span class="muted"><?= htmlspecialchars($user['username']) ?> · <?= $user['role'] === 'admin' ? '管理员' : '营销' ?></span>
  <span class="muted" id="balBox" style="display:none">许可证：<span class="balance" id="balNum">0</span> 点</span>
  <span style="flex:1"></span>
  <a href="/logout.php" class="btn gray" style="text-decoration:none;color:#cfd8ea">退出</a>
</header>
<nav id="nav"></nav>
<main>
  <div class="tab on" id="tab-card"><div id="cardBox">加载中...</div></div>
  <div class="tab" id="tab-sales"><div id="salesBox">加载中...</div></div>
  <div class="tab" id="tab-mkt"><div id="mktBox">加载中...</div></div>
  <div class="tab" id="tab-logs"><div id="logsBox">加载中...</div></div>
</main>
<div id="toast" style="display:none;position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#1d7a45;color:#fff;padding:10px 20px;border-radius:6px;z-index:200"></div>
<script>
const $ = s => document.querySelector(s);
const esc = s => s == null ? '' : String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const CSRF = '<?= htmlspecialchars($_SESSION['fk_csrf']) ?>';
const IS_ADMIN = <?= $user['role'] === 'admin' ? 'true' : 'false' ?>;
const TIERS = <?= json_encode(FK_TIERS, JSON_UNESCAPED_UNICODE) ?>;   // {tier: {name, price:{month,year,life}}}
const POINT_YUAN = <?= FK_POINT_YUAN ?>;

async function api(path, opts = {}) {
  const r = await fetch(path, { headers: {'Content-Type':'application/json','X-FK-CSRF':CSRF}, ...opts });
  const j = await r.json().catch(() => ({}));
  if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
  return j;
}
function toast(msg, ok = true) {
  const t = $('#toast');
  t.textContent = msg; t.style.background = ok ? '#1d7a45' : '#a13a3a'; t.style.display = 'block';
  clearTimeout(t._h); t._h = setTimeout(() => t.style.display = 'none', 3000);
}
function fmtDate(s) {
  if (!s || s <= 0) return '—';
  let ms = typeof s === 'string' && isNaN(s) ? s : s;
  if (typeof s === 'string' && !/^\d+$/.test(s)) return s;
  ms = Number(s); if (ms < 1e12) ms *= 1000;
  return new Date(ms).toLocaleString();
}
function tierName(t) { return (TIERS[t] && TIERS[t].name) || (t == 9 ? '终身卡' : ('档位' + t)); }
function payName(p) { return {month:'月卡', year:'年卡', life:'终身'}[p] || p || '—'; }
function tierPrice(t, p) { return (TIERS[t] && TIERS[t].price[p]) || 0; }
function costOfTier(t, p) { return Math.ceil(tierPrice(t, p) / POINT_YUAN); }

/* ── 导航 ── */
const NAV = [['card','开卡 / 续费'], ['sales','开卡记录']];
if (IS_ADMIN) NAV.push(['mkt','营销管理'], ['logs','授权流水']);
function buildNav() {
  $('#nav').innerHTML = NAV.map(([k, label], i) =>
    '<button data-k="' + k + '" class="' + (i === 0 ? 'on' : '') + '" onclick="loadTab(\'' + k + '\',this)">' + label + '</button>').join('');
}
function loadTab(k, btn) {
  document.querySelectorAll('#nav button').forEach(b => b.classList.remove('on'));
  if (btn) btn.classList.add('on');
  document.querySelectorAll('.tab').forEach(t => t.classList.remove('on'));
  $('#tab-' + k).classList.add('on');
  if (k === 'card') loadBalance();
  else if (k === 'sales') loadSales();
  else if (k === 'mkt') loadMarketers();
  else if (k === 'logs') loadGrantLog();
}

/* ── 余额 ── */
async function loadBalance() {
  const j = await api('?action=me');
  if (j.role === 'admin') { $('#balBox').style.display = 'none'; }
  else { $('#balBox').style.display = ''; $('#balNum').textContent = j.balance; }
}

/* ── 开卡 / 续费 ── */
let checkCache = null;
async function checkUser() {
  const name = $('#cName').value.trim();
  const info = $('#cInfo');
  checkCache = null;
  if (name.length < 3) { info.textContent = ''; return; }
  try {
    const j = await api('?action=check_user', {method:'POST', body:JSON.stringify({name})});
    if (!j.exists) { info.innerHTML = '<span class="badge">新用户 · 将执行「开卡」</span>'; return; }
    if (j.banned) { info.innerHTML = '<span class="badge red">该账号已被封禁，无法操作</span>'; return; }
    if (j.role === 'admin') { info.innerHTML = '<span class="badge red">管理员账号无需开卡</span>'; return; }
    if (j.is_life) { info.innerHTML = '<span class="badge green">终身卡 · 无需续费</span>'; return; }
    checkCache = {tier: j.tier};
    const expTxt = j.expires ? fmtDate(j.expires) : '无';
    info.innerHTML = '<span class="badge green">已存在 · ' + esc(tierName(j.tier)) + ' / ' + payName(j.pay_type) + '</span>'
      + ' <span class="muted">到期：' + expTxt + ' · 将执行「续费」（需同档位）</span>';
    if (j.tier) $('#cTier').value = String(j.tier);
  } catch (e) { info.innerHTML = '<span class="badge red">' + esc(e.message) + '</span>'; }
}
function costOf() {
  const tier = +$('#cTier').value;
  if (tier === 3) return costOfTier(3, 'life');
  const months = +$('#cMonths').value;
  return costOfTier(tier, months >= 12 ? 'year' : 'month');
}
function syncTier() {
  const life = $('#cTier').value === '3';
  $('#monthsWrap').style.display = life ? 'none' : '';
  const t = +$('#cTier').value;
  $('#costTxt').textContent = '消耗：' + costOf() + ' 点（¥' + tierPrice(t, life ? 'life' : (+$('#cMonths').value >= 12 ? 'year' : 'month')) + '）';
}
async function doSubmit() {
  const name = $('#cName').value.trim(), pwd = $('#cPwd').value;
  const tier = +$('#cTier').value, months = tier === 3 ? 0 : +$('#cMonths').value;
  if (name.length < 3) { toast('用户名需 3-32 位', false); return; }
  const isRenew = checkCache && checkCache.tier > 0;
  if (!isRenew && pwd.length < 6) { toast('新账号密码至少 6 位', false); return; }
  try {
    const body = isRenew ? {name, tier, months} : {name, pwd, tier, months};
    const j = await api('?action=' + (isRenew ? 'renew' : 'open'), {method:'POST', body:JSON.stringify(body)});
    toast((isRenew ? '续费成功，扣除 ' : '开卡成功，扣除 ') + j.cost + ' 点，当前余额 ' + j.balance + ' 点');
    $('#cPwd').value = '';
    $('#balNum').textContent = j.balance;
    checkCache = null;
    $('#cInfo').innerHTML = '<span class="badge green">完成 · 新到期：' + fmtDate(j.expires) + '</span>';
  } catch (e) { toast(e.message, false); }
}
function renderCard() {
  $('#cardBox').innerHTML = '<div class="card"><h3>添加账号 / 续费</h3>'
    + '<div class="row"><div><label class="fk">用户名</label><input id="cName" style="width:200px" placeholder="3-32 位" oninput="checkUser()"></div>'
    + '<div><label class="fk">档位</label><select id="cTier" onchange="syncTier()">'
    + Object.entries(TIERS).map(([v, t]) => '<option value="' + v + '">' + esc(t.name) + '</option>').join('') + '</select></div>'
    + '<div id="monthsWrap"><label class="fk">时长</label><select id="cMonths" onchange="syncTier()"><option value="1">月卡</option><option value="13">年卡</option></select></div>'
    + '<div id="pwdWrap"><label class="fk">初始密码</label><input id="cPwd" type="password" style="width:160px" autocomplete="new-password"></div>'
    + '<div style="align-self:flex-end"><button class="btn green" onclick="doSubmit()">确认提交</button></div></div>'
    + '<div id="costTxt" style="color:#2ecc71;font-weight:bold;margin:4px 0"></div>'
    + '<div class="muted" style="font-size:12px">白银（有头·单开） ¥20/月 · ¥199/年（1点/10点） ｜ 黄金（有头+无头·双开） ¥35/月 · ¥349/年（2点/18点） ｜ 终身卡·全部含云端 ¥888（45点·永久）</div>'
    + '<div id="cInfo" class="muted" style="min-height:20px"></div>'
    + '<div class="muted" style="margin-top:6px">已存在的用户将自动走「续费」（扣点叠加时长，需同档位）；新用户开卡直接写入账号体系，控制台立即可用。</div></div>';
  syncTier();
}

/* ── 开卡记录 ── */
async function loadSales() {
  try {
    const j = await api('?action=' + (IS_ADMIN ? 'all_sales' : 'my_sales'));
    let html = '<div class="card"><h3>' + (IS_ADMIN ? '全部开卡记录' : '我的开卡记录') + ' <button class="btn gray sm" style="float:right" onclick="loadSales()">刷新</button></h3>';
    if (!j.sales.length) { html += '<div class="muted">暂无记录</div>'; }
    else {
      html += '<table><tr><th>时间</th>' + (IS_ADMIN ? '<th>营销</th>' : '') + '<th>操作</th><th>账号</th><th>档位</th><th>类型</th><th>扣点</th><th>到期</th></tr>';
      for (const s of j.sales) {
        html += '<tr><td>' + esc(s.created_at) + '</td>' + (IS_ADMIN ? '<td>' + esc(s.marketer_name) + '</td>' : '')
          + '<td>' + (s.action === 'open' ? '开卡' : '续费') + '</td><td>' + esc(s.target_name) + '</td>'
          + '<td>' + esc(tierName(s.tier)) + '</td><td>' + payName(s.pay_type) + '</td>'
          + '<td>' + s.cost + '</td><td>' + fmtDate(s.expires) + '</td></tr>';
      }
      html += '</table>';
    }
    html += '</div>';
    $('#salesBox').innerHTML = html;
  } catch (e) { $('#salesBox').innerHTML = '<div style="color:#c0392b">加载失败: ' + esc(e.message) + '</div>'; }
}

/* ── 营销管理（管理员）── */
let mktCache = [];
async function loadMarketers() {
  try {
    const j = await api('?action=list_marketers');
    mktCache = j.marketers || [];
    let html = '<div class="card"><h3>营销人员 <button class="btn gray sm" style="float:right" onclick="loadMarketers()">刷新</button></h3>';
    html += '<div class="row"><div><label class="fk">将已有用户设为营销</label><input id="mName" style="width:180px" placeholder="用户名"></div>'
      + '<div style="align-self:flex-end"><button class="btn green" onclick="setMarketer(1)">设为营销</button></div></div>';
    if (!mktCache.length) { html += '<div class="muted">暂无营销人员</div>'; }
    else {
      html += '<table><tr><th>ID</th><th>用户名</th><th>许可证余额</th><th>累计开卡</th><th>累计续费</th><th>最近登录</th><th>操作</th></tr>';
      for (const m of mktCache) {
        html += '<tr><td>' + m.id + '</td><td>' + esc(m.username) + '</td><td><b style="color:#2ecc71">' + m.balance + '</b> 点</td>'
          + '<td>' + m.opened + '</td><td>' + m.renewed + '</td><td>' + esc(m.last_login || '—') + '</td><td>'
          + '<button class="btn green sm" onclick="openGrantDlg(' + m.id + ')">发放/回收授权</button> '
          + '<button class="btn red sm" onclick="setMarketer(0, ' + m.id + ')">取消营销</button></td></tr>';
      }
      html += '</table>';
    }
    html += '</div>';
    $('#mktBox').innerHTML = html;
  } catch (e) { $('#mktBox').innerHTML = '<div style="color:#c0392b">加载失败: ' + esc(e.message) + '</div>'; }
}
async function setMarketer(on, uid) {
  try {
    let name;
    if (uid) { const m = mktCache.find(x => x.id === uid); if (!confirm('取消 ' + (m ? m.username : uid) + ' 的营销身份？其余额保留但不发放。')) return; name = m.username; }
    else { name = $('#mName').value.trim(); if (!name) { toast('请输入用户名', false); return; } }
    await api('?action=set_marketer', {method:'POST', body:JSON.stringify({name, on})});
    toast(on ? '已设为营销' : '已取消营销身份');
    loadMarketers();
  } catch (e) { toast(e.message, false); }
}
function openGrantDlg(uid) {
  const m = mktCache.find(x => x.id === uid);
  const html = '<h3>发放 / 回收授权 — ' + esc(m.username) + '（当前 ' + m.balance + ' 点）</h3>'
    + '<label class="fk">变动数量（正=发放，负=回收）</label><input id="gDelta" type="number" value="10" style="width:120px">'
    + '<label class="fk">备注（可选）</label><input id="gNote" style="width:100%">'
    + '<button class="btn green" style="margin-top:12px" onclick="doGrant(' + uid + ')">确认</button>';
  $('#mktBox').insertAdjacentHTML('beforeend',
    '<div id="dlgMask" style="position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:100;display:flex;align-items:center;justify-content:center" onclick="if(event.target===this)this.remove()">'
    + '<div style="background:#161d30;border:1px solid #2c3a55;border-radius:8px;padding:16px;max-width:420px;width:90%">' + html + '</div></div>');
}
async function doGrant(uid) {
  const delta = +$('#gDelta').value, note = $('#gNote').value.trim();
  try {
    const j = await api('?action=grant_credit', {method:'POST', body:JSON.stringify({uid, delta, note})});
    toast('操作成功，余额 ' + j.balance + ' 点');
    document.getElementById('dlgMask').remove();
    loadMarketers();
  } catch (e) { toast(e.message, false); }
}

/* ── 授权流水（管理员）── */
async function loadGrantLog() {
  try {
    const j = await api('?action=grant_log');
    let html = '<div class="card"><h3>授权发放流水 <button class="btn gray sm" style="float:right" onclick="loadGrantLog()">刷新</button></h3>';
    if (!j.logs.length) { html += '<div class="muted">暂无记录</div>'; }
    else {
      html += '<table><tr><th>时间</th><th>操作管理员</th><th>营销</th><th>变动</th><th>变动后余额</th><th>备注</th></tr>';
      for (const g of j.logs) {
        html += '<tr><td>' + esc(g.created_at) + '</td><td>' + esc(g.admin_name) + '</td><td>' + esc(g.target_name) + '</td>'
          + '<td><span class="badge ' + (g.delta >= 0 ? 'green' : 'red') + '">' + (g.delta >= 0 ? '+' : '') + g.delta + '</span></td>'
          + '<td>' + g.balance + '</td><td>' + esc(g.note || '—') + '</td></tr>';
      }
      html += '</table>';
    }
    html += '</div>';
    $('#logsBox').innerHTML = html;
  } catch (e) { $('#logsBox').innerHTML = '<div style="color:#c0392b">加载失败: ' + esc(e.message) + '</div>'; }
}

/* ── 初始化 ── */
buildNav();
renderCard();
(async () => {
  const j = await api('?action=me');
  if (j.role !== 'admin') { $('#balBox').style.display = ''; $('#balNum').textContent = j.balance; }
})();
loadBalance();
</script>
</body>
</html>
