{% extends "base.html" %}
{% set active = "admin" %}

{% block title %}Panel Admin{% endblock %}
{% block page_title %}Panel Admin{% endblock %}

{% block extra_head %}
<style>
  /* ── Tabs ── */
  .admin-tabs {
    display: flex; gap: 2px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 3px;
    margin-bottom: 24px;
    width: fit-content;
    flex-wrap: wrap;
  }
  .admin-tab {
    padding: 7px 18px;
    border: none; background: none;
    color: var(--muted);
    font-family: 'DM Sans', sans-serif;
    font-size: .85rem; font-weight: 600;
    cursor: pointer;
    border-radius: 6px;
    transition: all .15s;
    display: flex; align-items: center; gap: 6px;
  }
  .admin-tab.active {
    background: var(--surface2);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(0,0,0,.3);
  }
  .tab-panel { display: none; }
  .tab-panel.show { display: block; }

  /* ── Stats ── */
  .stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 14px;
    margin-bottom: 24px;
  }
  .stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px;
  }
  .stat-top {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 10px;
  }
  .stat-label { font-size: .75rem; color: var(--muted); text-transform: uppercase; font-weight: 600; letter-spacing: .5px; }
  .stat-icon  { font-size: 18px; }
  .stat-num   { font-family: 'Sora', sans-serif; font-size: 1.8rem; font-weight: 800; }
  .stat-sub   { font-size: .75rem; color: var(--muted); margin-top: 4px; }

  /* ── Toolbar ── */
  .toolbar {
    display: flex; align-items: center; gap: 10px;
    margin-bottom: 16px; flex-wrap: wrap;
  }
  .toolbar-right { margin-left: auto; display: flex; gap: 8px; }

  /* ── Table ── */
  .table-wrap { overflow-x: auto; }
  table { width: 100%; border-collapse: collapse; font-size: .84rem; }
  thead tr { border-bottom: 1px solid var(--border); }
  th {
    text-align: left; padding: 10px 14px;
    font-size: .72rem; font-weight: 600;
    color: var(--muted); text-transform: uppercase; letter-spacing: .5px;
  }
  td { padding: 11px 14px; border-bottom: 1px solid var(--border2); vertical-align: middle; }
  tbody tr:hover { background: var(--surface2); }
  tbody tr:last-child td { border-bottom: none; }
  .truncate { max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  .result-snippet {
    font-size: .76rem;
    color: var(--muted);
    font-style: italic;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
    margin-top: 3px;
  }
  .btn-lihat-hasil {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 3px 10px;
    background: rgba(88,166,255,.1);
    border: 1px solid rgba(88,166,255,.25);
    border-radius: 20px;
    color: var(--accent);
    font-size: .72rem; font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: all .15s;
    font-family: 'DM Sans', sans-serif;
  }
  .btn-lihat-hasil:hover { background: rgba(88,166,255,.2); }

  /* ── Modals ── */
  .modal-backdrop {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0,0,0,.7);
    z-index: 200;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .modal-backdrop.show { display: flex; }
  .modal {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 28px;
    width: 100%;
    max-width: 460px;
    animation: modal-in .2s ease;
  }
  .modal-result {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 24px;
    width: 100%;
    max-width: 700px;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    animation: modal-in .2s ease;
  }
  @keyframes modal-in {
    from { opacity: 0; transform: scale(.96) translateY(10px); }
    to   { opacity: 1; transform: scale(1) translateY(0); }
  }
  .modal-header {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 20px;
  }
  .modal-title { font-family: 'Sora', sans-serif; font-size: 1rem; font-weight: 700; }
  .modal-close {
    background: none; border: none;
    color: var(--muted); cursor: pointer; font-size: 1.2rem;
    padding: 2px 6px; border-radius: 4px; transition: all .15s;
  }
  .modal-close:hover { background: var(--surface2); color: var(--text); }
  .modal-footer {
    display: flex; gap: 8px; justify-content: flex-end;
    margin-top: 20px; padding-top: 16px;
    border-top: 1px solid var(--border2);
  }
  .result-modal-body { flex: 1; overflow-y: auto; }
  .result-meta-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 8px; margin-bottom: 14px;
  }
  .result-meta-item {
    background: var(--bg);
    border: 1px solid var(--border2);
    border-radius: var(--radius-sm);
    padding: 8px 12px; font-size: .78rem;
  }
  .result-meta-key { color: var(--muted); margin-bottom: 2px; }
  .result-meta-val { font-weight: 600; }
  .result-textarea {
    width: 100%;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 14px;
    border-radius: var(--radius-sm);
    font-family: 'DM Sans', sans-serif;
    font-size: .875rem; line-height: 1.8;
    resize: vertical; min-height: 260px; outline: none;
  }
  .result-textarea:focus { border-color: var(--accent); }
  .char-info { font-size: .72rem; color: var(--muted); margin-top: 6px; }

  .filter-select {
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 7px 12px;
    border-radius: var(--radius-sm);
    font-family: 'DM Sans', sans-serif;
    font-size: .82rem; outline: none; cursor: pointer;
  }
  .empty-row td {
    text-align: center; padding: 40px;
    color: var(--muted); font-size: .875rem;
  }

  /* ── Modal Overlay (preview prompt) ── */
  .modal-overlay {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0,0,0,.7);
    z-index: 200;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .modal-overlay.show { display: flex; }
  .modal-box {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 24px;
    width: 100%;
    max-width: 760px;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    animation: modal-in .2s ease;
  }
  .modal-body { flex: 1; overflow-y: auto; margin: 16px 0; }

  /* ── Toast ── */
  .admin-toast {
    position: fixed;
    bottom: 20px; right: 20px;
    background: var(--surface);
    border-left: 4px solid var(--accent);
    padding: 12px 18px;
    border-radius: var(--radius-sm);
    box-shadow: 0 4px 12px rgba(0,0,0,.3);
    font-size: .85rem;
    z-index: 300;
    animation: toast-in .2s ease;
  }
  @keyframes toast-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  /* ── User Search ── */
  .user-search-container { position: relative; }
  .user-search-results {
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    max-height: 200px; overflow-y: auto;
    z-index: 100; display: none;
  }
  .user-search-results div {
    padding: 8px 12px; cursor: pointer; transition: background 0.15s;
  }
  .user-search-results div:hover { background: var(--surface2); }
  .selected-user-badge {
    background: rgba(63,185,80,.12);
    border: 1px solid rgba(63,185,80,.25);
    border-radius: var(--radius-sm);
    padding: 8px 12px; margin-top: 8px;
    display: flex; align-items: center; justify-content: space-between;
  }
  .saldo-cell {
    font-size: .82rem; font-weight: 600;
    color: var(--success); white-space: nowrap;
  }
  .saldo-cell.zero { color: var(--muted); font-weight: 400; }

  /* ── BA Prompt Tab ── */
  .prompt-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin-bottom: 14px;
  }
  .prompt-stat-box {
    background: var(--bg);
    border: 1px solid var(--border2);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
  }
  .prompt-stat-label {
    font-size: .68rem; color: var(--muted);
    font-weight: 600; text-transform: uppercase;
    letter-spacing: .5px; margin-bottom: 4px;
  }
  .prompt-stat-val {
    font-family: 'Sora', sans-serif;
    font-size: 1.25rem; font-weight: 800; color: var(--text);
  }
  .prompt-stat-sub { font-size: .68rem; color: var(--muted); margin-top: 2px; }

  .prompt-status-bar {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 14px; flex-wrap: wrap; gap: 8px;
  }
  .prompt-status-badge {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 10px;
    border-radius: 20px;
    font-size: .72rem; font-weight: 700;
  }
  .prompt-status-badge.is-default {
    background: rgba(107,114,128,.1);
    border: 1px solid rgba(107,114,128,.2);
    color: var(--muted);
  }
  .prompt-status-badge.is-custom {
    background: rgba(88,166,255,.12);
    border: 1px solid rgba(88,166,255,.25);
    color: var(--accent);
  }

  .prompt-tips {
    list-style: none; padding: 0;
    display: flex; flex-direction: column; gap: 8px;
  }
  .prompt-tips li {
    display: flex; align-items: flex-start; gap: 8px;
    font-size: .82rem; color: var(--text); line-height: 1.5;
  }
  .prompt-tips li::before {
    content: '→';
    color: var(--accent);
    font-weight: 700;
    flex-shrink: 0;
    margin-top: 1px;
  }
  .prompt-tips code {
    background: var(--surface2);
    padding: 1px 6px;
    border-radius: 3px;
    font-family: 'DM Mono', monospace;
    font-size: .78rem;
    color: var(--accent);
  }

  .prompt-char-bar {
    display: flex; align-items: center;
    justify-content: space-between;
    margin-top: 8px; font-size: .72rem; color: var(--muted);
    flex-wrap: wrap; gap: 6px;
  }
  .token-badge {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px;
    border-radius: 20px;
    font-size: .7rem; font-weight: 700;
  }
  .token-badge.ok {
    background: rgba(63,185,80,.1);
    border: 1px solid rgba(63,185,80,.25);
    color: var(--success);
  }
  .token-badge.warn {
    background: rgba(210,153,34,.1);
    border: 1px solid rgba(210,153,34,.25);
    color: var(--warning);
  }
  .token-badge.bad {
    background: rgba(248,81,73,.1);
    border: 1px solid rgba(248,81,73,.25);
    color: var(--danger);
  }

  /* ── Feature Toggle Cards ── */
  .features-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 14px;
  }
  .feature-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px 20px;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    transition: border-color .2s, box-shadow .2s;
  }
  .feature-card:hover {
    border-color: var(--accent);
    box-shadow: 0 2px 10px var(--accent-bg);
  }
  .feature-card.disabled-card {
    opacity: .7;
    border-color: var(--danger);
  }
  .feature-card.disabled-card:hover {
    border-color: var(--danger);
    box-shadow: 0 2px 10px rgba(204,42,34,.08);
  }
  [data-theme="dark"] .feature-card.disabled-card:hover {
    box-shadow: 0 2px 10px rgba(248,81,73,.08);
  }
  .feature-icon {
    font-size: 1.6rem;
    flex-shrink: 0;
    margin-top: 2px;
    line-height: 1;
  }
  .feature-info { flex: 1; min-width: 0; }
  .feature-label {
    font-weight: 700;
    font-size: .9rem;
    margin-bottom: 4px;
    line-height: 1.3;
    color: var(--text);
  }
  .feature-desc {
    font-size: .78rem;
    color: var(--muted);
    line-height: 1.5;
  }
  .feature-status-badge {
    font-size: .68rem;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 20px;
    margin-top: 8px;
    display: inline-block;
    border: 1px solid transparent;
  }
  .feature-status-badge.on {
    background: rgba(26,122,58,.10);
    color: var(--success);
    border-color: rgba(26,122,58,.25);
  }
  .feature-status-badge.off {
    background: rgba(204,42,34,.10);
    color: var(--danger);
    border-color: rgba(204,42,34,.25);
  }
  [data-theme="dark"] .feature-status-badge.on {
    background: rgba(63,185,80,.12);
    border-color: rgba(63,185,80,.3);
  }
  [data-theme="dark"] .feature-status-badge.off {
    background: rgba(248,81,73,.12);
    border-color: rgba(248,81,73,.3);
  }

  /* Toggle switch */
  .toggle-wrap {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
  }
  .toggle-label-small {
    font-size: .66rem;
    color: var(--muted);
    font-weight: 700;
    letter-spacing: .5px;
  }
  .toggle-switch {
    position: relative;
    width: 46px; height: 26px;
    cursor: pointer;
    flex-shrink: 0;
  }
  .toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
  .toggle-track {
    position: absolute; inset: 0;
    background: var(--border);
    border-radius: 26px;
    transition: background .2s;
    border: 1px solid rgba(0,0,0,.05);
  }
  .toggle-switch input:checked + .toggle-track {
    background: var(--success);
  }
  .toggle-track::after {
    content: '';
    position: absolute;
    top: 3px; left: 3px;
    width: 18px; height: 18px;
    background: #fff;
    border-radius: 50%;
    transition: transform .2s;
    box-shadow: 0 1px 3px rgba(0,0,0,.3);
  }
  .toggle-switch input:checked + .toggle-track::after {
    transform: translateX(20px);
  }
  .toggle-switch.busy { cursor: wait; }
  .toggle-switch.busy .toggle-track { opacity: .55; }
  .features-refresh-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
    flex-wrap: wrap;
    gap: 8px;
  }
  .features-last-refresh { font-size: .78rem; color: var(--muted); }

  /* ══════════════════════════════════════════════
     STORAGE TAB CSS
  ══════════════════════════════════════════════ */

  /* ── Storage: stats bar ── */
  .storage-stats-bar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-bottom: 20px;
  }
  .storage-stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
  }
  .storage-stat-label {
    font-size: .7rem; color: var(--muted);
    text-transform: uppercase; font-weight: 600;
    letter-spacing: .5px; margin-bottom: 6px;
  }
  .storage-stat-val {
    font-family: 'Sora', sans-serif;
    font-size: 1.4rem; font-weight: 800;
  }
  .storage-stat-sub { font-size: .72rem; color: var(--muted); margin-top: 2px; }

  /* ── Storage: file size badge ── */
  .file-size-badge {
    display: inline-flex; align-items: center;
    padding: 2px 8px;
    border-radius: 20px;
    font-size: .72rem; font-weight: 600;
    font-family: 'DM Mono', monospace;
    white-space: nowrap;
  }
  .file-size-badge.exists {
    background: rgba(63,185,80,.1);
    border: 1px solid rgba(63,185,80,.2);
    color: var(--success);
  }
  .file-size-badge.missing {
    background: rgba(248,81,73,.08);
    border: 1px solid rgba(248,81,73,.2);
    color: var(--danger);
  }

  /* ── Storage: checkbox select ── */
  .storage-select-all { cursor: pointer; }
  .storage-row-check  { cursor: pointer; }
  .storage-row.selected td { background: rgba(88,166,255,.04); }

  /* ── Storage: bulk action bar ── */
  .bulk-action-bar {
    display: none;
    align-items: center; gap: 10px;
    padding: 10px 14px;
    background: rgba(88,166,255,.06);
    border: 1px solid rgba(88,166,255,.2);
    border-radius: var(--radius-sm);
    margin-bottom: 12px;
    flex-wrap: wrap;
  }
  .bulk-action-bar.show { display: flex; }
  .bulk-count {
    font-size: .82rem; font-weight: 600; color: var(--accent);
  }

  /* ── Storage: filter row ── */
  .storage-filter-row {
    display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
    margin-bottom: 14px;
  }

  /* ── Danger zone card ── */
  .danger-zone-card {
    background: rgba(248,81,73,.04);
    border: 1px solid rgba(248,81,73,.2);
    border-radius: var(--radius);
    padding: 18px 20px;
    margin-top: 20px;
  }
  .danger-zone-title {
    font-family: 'Sora', sans-serif;
    font-size: .88rem; font-weight: 700;
    color: var(--danger);
    margin-bottom: 12px;
    display: flex; align-items: center; gap: 6px;
  }
  .danger-zone-desc {
    font-size: .82rem; color: var(--muted); margin-bottom: 12px; line-height: 1.6;
  }
</style>
{% endblock %}

{% block topbar_extra %}
<div style="margin-left:auto">
  <span class="badge badge-admin">👑 Admin</span>
</div>
{% endblock %}

{% block content %}
<!-- Tabs -->
<div class="admin-tabs">
  <button class="admin-tab active" onclick="switchTab('stats', this)">📊 Statistik</button>
  <button class="admin-tab" onclick="switchTab('tasks', this)">📋 Tasks</button>
  <button class="admin-tab" onclick="switchTab('users', this)">👥 Pengguna</button>
  <button class="admin-tab" onclick="switchTab('ba-prompt', this)">⚙️ Prompt Berita Acara</button>
  <button class="admin-tab" onclick="switchTab('balance', this)">💰 Saldo & Tarif</button>
  <button class="admin-tab" onclick="switchTab('features', this)">🔧 Kelola Fitur</button>
  <button class="admin-tab" onclick="switchTab('storage', this); loadStorageStats(); loadStorage()">📁 File Audio</button>
</div>

<!-- ══════════════════════════════════════════════
     TAB: Statistik
═══════════════════════════════════════════════ -->
<div class="tab-panel show" id="tab-stats">
  <div class="stats-grid">
    <div class="stat-card">
      <div class="stat-top"><span class="stat-label">Total Task</span><span class="stat-icon">📋</span></div>
      <div class="stat-num" id="s-total">—</div>
    </div>
    <div class="stat-card">
      <div class="stat-top"><span class="stat-label">Selesai</span><span class="stat-icon">✅</span></div>
      <div class="stat-num" id="s-done">—</div>
    </div>
    <div class="stat-card">
      <div class="stat-top"><span class="stat-label">Antrian</span><span class="stat-icon">⏳</span></div>
      <div class="stat-num" id="s-pending">—</div>
    </div>
    <div class="stat-card">
      <div class="stat-top"><span class="stat-label">Gagal</span><span class="stat-icon">❌</span></div>
      <div class="stat-num" id="s-failed">—</div>
    </div>
    <div class="stat-card">
      <div class="stat-top"><span class="stat-label">Total User</span><span class="stat-icon">👥</span></div>
      <div class="stat-num" id="s-users">—</div>
      <div class="stat-sub" id="s-users-active"></div>
    </div>
    <div class="stat-card">
      <div class="stat-top"><span class="stat-label">Total Durasi</span><span class="stat-icon">🎵</span></div>
      <div class="stat-num" id="s-duration">—</div>
      <div class="stat-sub">audio ditranskripsi</div>
    </div>
  </div>
  <div class="alert alert-info">
    <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
      <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/>
      <line x1="12" y1="16" x2="12.01" y2="16"/>
    </svg>
    Data statistik diperbarui setiap kali halaman dimuat.
  </div>
</div>

<!-- ══════════════════════════════════════════════
     TAB: Tasks
═══════════════════════════════════════════════ -->
<div class="tab-panel" id="tab-tasks">
  <div class="toolbar">
    <select class="filter-select" id="task-filter" onchange="loadTasks()">
      <option value="">Semua Status</option>
      <option value="pending">Menunggu</option>
      <option value="processing">Diproses</option>
      <option value="completed">Selesai</option>
      <option value="failed">Gagal</option>
    </select>
    <button class="btn btn-ghost btn-sm" onclick="loadTasks()">🔄 Refresh</button>
    <div class="toolbar-right">
      <button class="btn btn-danger btn-sm" onclick="deleteAllTasks()">🗑 Hapus Semua Selesai</button>
    </div>
  </div>
  <div class="card">
    <div class="table-wrap">
      <table>
        <thead>
          <tr>
            <th>ID</th><th>Status</th><th>Engine / Model</th>
            <th>Hasil Transkripsi</th><th>Durasi</th><th>Dibuat</th><th>Aksi</th>
          </tr>
        </thead>
        <tbody id="tasks-tbody">
          <tr class="empty-row"><td colspan="7">Memuat data...</td></tr>
        </tbody>
      </table>
    </div>
    <div style="padding:12px 14px;border-top:1px solid var(--border2);font-size:.78rem;color:var(--muted)" id="tasks-info"></div>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     TAB: Pengguna
═══════════════════════════════════════════════ -->
<div class="tab-panel" id="tab-users">
  <div class="toolbar">
    <button class="btn btn-ghost btn-sm" onclick="loadUsers()">🔄 Refresh</button>
    <div class="toolbar-right">
      <button class="btn btn-primary btn-sm" onclick="openAddUser()">+ Tambah Pengguna</button>
    </div>
  </div>
  <div class="card">
    <div class="table-wrap">
      <table>
        <thead>
          <tr>
            <th>ID</th><th>Username</th><th>Nama</th><th>Email</th>
            <th>Role</th><th>Status</th><th>Saldo</th><th>Dibuat</th><th></th>
          </tr>
        </thead>
        <tbody id="users-tbody">
          <tr class="empty-row"><td colspan="9">Memuat data...</td></tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     TAB: Prompt Berita Acara
═══════════════════════════════════════════════ -->
<div class="tab-panel" id="tab-ba-prompt">

  <!-- Card 1: Status prompt aktif -->
  <div class="card" style="margin-bottom:16px">
    <div class="card-header" style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px">
      <span class="card-title">Status Prompt Aktif</span>
      <span id="prompt-status-badge" class="prompt-status-badge is-default">● Default</span>
    </div>

    <div class="prompt-stat-grid">
      <div class="prompt-stat-box">
        <div class="prompt-stat-label">Estimasi Token</div>
        <div class="prompt-stat-val" id="pstat-token">—</div>
        <div class="prompt-stat-sub">prompt saja (tanpa transkrip)</div>
      </div>
      <div class="prompt-stat-box">
        <div class="prompt-stat-label">Jumlah Karakter</div>
        <div class="prompt-stat-val" id="pstat-chars">—</div>
        <div class="prompt-stat-sub">panjang teks prompt</div>
      </div>
      <div class="prompt-stat-box">
        <div class="prompt-stat-label">Terakhir Diperbarui</div>
        <div class="prompt-stat-val" style="font-size:.85rem;font-weight:600" id="pstat-updated">—</div>
        <div class="prompt-stat-sub" id="pstat-updated-sub"></div>
      </div>
    </div>

    <div class="alert" id="prompt-ph-alert" style="margin-bottom:0">
      <svg width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
        <circle cx="12" cy="12" r="10"/>
        <line x1="12" y1="8" x2="12" y2="12"/>
        <line x1="12" y1="16" x2="12.01" y2="16"/>
      </svg>
      <span id="prompt-ph-msg">Memeriksa placeholder...</span>
    </div>
  </div>

  <!-- Card 2: Editor prompt -->
  <div class="card" style="margin-bottom:16px">
    <div class="card-header" style="margin-bottom:12px">
      <span class="card-title">⚙️ Edit Prompt — Berita Acara Sidang</span>
    </div>

    <div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:8px">
      <label class="form-label" style="margin:0">Teks Prompt</label>
      <div style="display:flex;gap:8px;align-items:center">
        <button class="btn btn-ghost btn-sm" onclick="loadDefaultPrompt()">↩ Reset Default</button>
        <button class="btn btn-ghost btn-sm" onclick="previewPrompt()">👁 Preview</button>
      </div>
    </div>

    <textarea
      id="ba-prompt-textarea"
      rows="16"
      style="font-family:'DM Mono',monospace;font-size:.8rem;line-height:1.7;resize:vertical;width:100%;
             background:var(--bg);border:1px solid var(--border);color:var(--text);
             padding:14px;border-radius:var(--radius-sm);outline:none;"
      placeholder="Masukkan prompt untuk pemrosesan Berita Acara..."></textarea>

    <div class="prompt-char-bar">
      <span id="prompt-char-count" style="color:var(--muted)"></span>
      <div style="display:flex;align-items:center;gap:8px">
        <span id="prompt-token-badge" class="token-badge ok" style="display:none"></span>
        <span id="prompt-updated-at" style="font-size:.72rem;color:var(--muted)"></span>
      </div>
    </div>

    <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:14px;padding-top:12px;border-top:1px solid var(--border2)">
      <button class="btn btn-ghost btn-sm" onclick="loadBAPrompt()">Batal</button>
      <button class="btn btn-primary btn-sm" onclick="saveBAPrompt()" id="btn-save-ba-prompt">
        <svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
          <path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v14a2 2 0 01-2 2z"/>
          <path d="M17 21v-8H7v8M7 3v5h8"/>
        </svg>
        Simpan Prompt
      </button>
    </div>
  </div>

  <!-- Card 3: Tips -->
  <div class="card">
    <div class="card-header" style="margin-bottom:14px">
      <span class="card-title">💡 Tips Prompt Efisien</span>
    </div>
    <ul class="prompt-tips">
      <li>Selalu sertakan <code>{transcript}</code> — tanpa ini transkrip tidak akan disisipkan ke prompt.</li>
      <li>Perintah singkat dan eksplisit lebih dipatuhi model daripada narasi panjang bertele-tele.</li>
      <li>Cantumkan contoh FORMAT JSON di dalam prompt agar output konsisten dan jarang butuh fallback parsing.</li>
      <li>Gunakan kata <strong style="font-weight:700">"saja"</strong> atau <strong style="font-weight:700">"HANYA"</strong> untuk mencegah model menambah teks di luar JSON.</li>
      <li>Target token prompt: di bawah 200 token. Makin hemat prompt, makin hemat biaya per proses Berita Acara.</li>
      <li>Perintahkan model mengganti tanda kutip ganda (<code>"</code>) di dalam teks menjadi kutip tunggal (<code>'</code>) agar JSON tidak rusak.</li>
    </ul>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     TAB: Saldo & Tarif
═══════════════════════════════════════════════ -->
<div class="tab-panel" id="tab-balance">

  <!-- Tambah Saldo -->
  <div class="card" style="margin-bottom:20px">
    <div class="card-header"><span class="card-title">💰 Tambah Saldo User</span></div>
    <div class="form-group">
      <label class="form-label">Cari Pengguna</label>
      <div class="user-search-container" id="topup-search-container">
        <input type="text" id="search_user_input" class="form-input"
          placeholder="Ketik username atau nama lengkap..." autocomplete="off">
        <div id="user_search_results" class="user-search-results"></div>
      </div>
      <input type="hidden" id="selected_user_id" value="">
      <div id="selected_user_display" style="display:none" class="selected-user-badge">
        <span>✅ <strong id="selected_user_name"></strong>
          (ID: <span id="selected_user_id_display"></span>)
          — Saldo: <strong id="selected_user_balance">—</strong>
        </span>
        <button type="button" onclick="clearSelectedUser()"
          style="background:none;border:none;color:var(--danger);cursor:pointer">✕</button>
      </div>
    </div>
    <div class="form-group">
      <label class="form-label">Jumlah (Token)</label>
      <input type="number" id="topup_amount" class="form-input" placeholder="Minimal 1 token" min="1" />
    </div>
    <div class="form-group">
      <label class="form-label">Deskripsi (opsional)</label>
      <input type="text" id="topup_desc" class="form-input" placeholder="Contoh: Bonus, top-up manual" />
    </div>
    <button class="btn btn-primary" onclick="addBalance()">Tambah Saldo</button>
  </div>

  <!-- Kurangi Saldo -->
  <div class="card" style="margin-bottom:20px">
    <div class="card-header"><span class="card-title">💸 Kurangi Saldo User</span></div>
    <div class="form-group">
      <label class="form-label">Cari Pengguna</label>
      <div class="user-search-container" id="deduct-search-container">
        <input type="text" id="deduct_search_user_input" class="form-input"
          placeholder="Ketik username atau nama lengkap..." autocomplete="off">
        <div id="deduct_user_search_results" class="user-search-results"></div>
      </div>
      <input type="hidden" id="deduct_selected_user_id" value="">
      <div id="deduct_selected_user_display" style="display:none" class="selected-user-badge">
        <span>✅ <strong id="deduct_selected_user_name"></strong>
          (ID: <span id="deduct_selected_user_id_display"></span>)
          — Saldo: <strong id="deduct_selected_user_balance">—</strong>
        </span>
        <button type="button" onclick="clearDeductSelectedUser()"
          style="background:none;border:none;color:var(--danger);cursor:pointer">✕</button>
      </div>
    </div>
    <div class="form-group">
      <label class="form-label">Jumlah yang Dikurangi (Token)</label>
      <input type="number" id="deduct_amount" class="form-input" placeholder="Minimal 1 token" min="1" />
    </div>
    <div class="form-group">
      <label class="form-label">Deskripsi (opsional)</label>
      <input type="text" id="deduct_desc" class="form-input" placeholder="Contoh: Koreksi saldo, pembayaran manual" />
    </div>
    <button class="btn btn-danger" onclick="deductBalance()">Kurangi Saldo</button>
  </div>

  <!-- Tarif -->
  <div class="card">
    <div class="card-header"><span class="card-title">⚙️ Atur Tarif Layanan</span></div>
    <div class="form-group">
      <label class="form-label">Tarif Transkripsi (token/menit)</label>
      <input type="number" id="rate_transcription" class="form-input" step="100" />
    </div>
    <div class="form-group">
      <label class="form-label">Tarif Berita Acara (token/proses)</label>
      <input type="number" id="rate_ba" class="form-input" step="5000" />
    </div>
    <button class="btn btn-primary" onclick="updateRates()">Simpan Tarif</button>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     TAB: Kelola Fitur
═══════════════════════════════════════════════ -->
<div class="tab-panel" id="tab-features">

  <div class="alert alert-info" style="margin-bottom:20px">
    <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
      <circle cx="12" cy="12" r="10"/>
      <line x1="12" y1="8" x2="12" y2="12"/>
      <line x1="12" y1="16" x2="12.01" y2="16"/>
    </svg>
    <span>
      Toggle di bawah menonaktifkan fitur untuk <strong>semua pengguna</strong> secara real-time.
      Pengguna yang mengakses fitur nonaktif akan mendapat pesan <em>maintenance</em>.
      Perubahan berlaku dalam hitungan detik tanpa perlu restart server.
    </span>
  </div>

  <div class="features-refresh-bar">
    <span class="features-last-refresh" id="features-last-refresh"></span>
    <button class="btn btn-ghost btn-sm" onclick="loadFeatures()">🔄 Refresh</button>
  </div>

  <div class="features-grid" id="features-grid">
    <div class="stat-card" style="grid-column:1/-1;text-align:center;padding:40px;color:var(--muted)">
      Memuat fitur...
    </div>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     TAB: File Audio
═══════════════════════════════════════════════ -->
<div class="tab-panel" id="tab-storage">

  <!-- Stats bar -->
  <div class="storage-stats-bar" id="storage-stats-bar">
    <div class="storage-stat">
      <div class="storage-stat-label">Total File</div>
      <div class="storage-stat-val" id="st-total-files">—</div>
      <div class="storage-stat-sub">di direktori upload</div>
    </div>
    <div class="storage-stat">
      <div class="storage-stat-label">Total Ukuran</div>
      <div class="storage-stat-val" id="st-total-mb">—</div>
      <div class="storage-stat-sub">MB terpakai</div>
    </div>
    <div class="storage-stat">
      <div class="storage-stat-label">File Orphan</div>
      <div class="storage-stat-val" id="st-orphan-files">—</div>
      <div class="storage-stat-sub" id="st-orphan-mb">tidak ada task</div>
    </div>
  </div>

  <!-- Filter row -->
  <div class="storage-filter-row">
    <select class="filter-select" id="storage-filter-status" onchange="loadStorage()">
      <option value="">Semua Status</option>
      <option value="completed">Selesai</option>
      <option value="failed">Gagal</option>
      <option value="cancelled">Dibatalkan</option>
      <option value="pending">Menunggu</option>
    </select>
    <select class="filter-select" id="storage-filter-file" onchange="loadStorage()">
      <option value="">Semua File</option>
      <option value="exists">File Ada</option>
      <option value="missing">File Hilang</option>
    </select>
    <button class="btn btn-ghost btn-sm" onclick="loadStorageStats(); loadStorage()">🔄 Refresh</button>
    <div style="margin-left:auto; display:flex; gap:8px;">
      <button class="btn btn-ghost btn-sm" onclick="previewOrphans()">🔍 Preview Orphan</button>
      <button class="btn btn-danger btn-sm" onclick="deleteOrphans()">🧹 Hapus Orphan</button>
    </div>
  </div>

  <!-- Bulk action bar (muncul kalau ada yang dicentang) -->
  <div class="bulk-action-bar" id="bulk-action-bar">
    <span class="bulk-count" id="bulk-count-label">0 dipilih</span>
    <button class="btn btn-ghost btn-sm" onclick="bulkDeleteFiles(false)">
      🗑 Hapus File Saja
    </button>
    <button class="btn btn-danger btn-sm" onclick="bulkDeleteFiles(true)">
      ❌ Hapus File + Task
    </button>
    <button class="btn btn-ghost btn-sm" style="margin-left:auto" onclick="clearStorageSelection()">
      Batal
    </button>
  </div>

  <!-- Table -->
  <div class="card">
    <div class="table-wrap">
      <table>
        <thead>
          <tr>
            <th style="width:36px">
              <input type="checkbox" class="storage-select-all" id="storage-select-all"
                     onchange="toggleSelectAllStorage(this.checked)" title="Pilih semua">
            </th>
            <th>Task</th>
            <th>Status</th>
            <th>File Audio</th>
            <th>Ukuran</th>
            <th>Durasi</th>
            <th>Dibuat</th>
            <th>Aksi</th>
          </tr>
        </thead>
        <tbody id="storage-tbody">
          <tr class="empty-row"><td colspan="8">Klik tab untuk memuat data...</td></tr>
        </tbody>
      </table>
    </div>
    <div style="padding:10px 14px;border-top:1px solid var(--border2);font-size:.76rem;color:var(--muted)"
         id="storage-info"></div>
  </div>

  <!-- Danger Zone -->
  <div class="danger-zone-card">
    <div class="danger-zone-title">⚠️ Zona Berbahaya</div>
    <div class="danger-zone-desc">
      Tindakan di bawah bersifat permanen dan tidak bisa dibatalkan.
      File yang dihapus tidak dapat dipulihkan.
    </div>
    <div style="display:flex; gap:8px; flex-wrap:wrap;">
      <button class="btn btn-danger btn-sm" onclick="deleteAllCompletedFiles()">
        🗑 Hapus Semua File Audio (Status: Selesai)
      </button>
      <button class="btn btn-danger btn-sm" onclick="deleteAllFiles()">
        💣 Hapus SEMUA File Audio
      </button>
    </div>
  </div>

</div><!-- end tab-storage -->

<!-- ══════════════════════════════════════════════
     Modal: Add/Edit User
═══════════════════════════════════════════════ -->
<div class="modal-backdrop" id="modal-user">
  <div class="modal">
    <div class="modal-header">
      <span class="modal-title" id="modal-title">Tambah Pengguna</span>
      <button class="modal-close" onclick="closeModal()">✕</button>
    </div>
    <form id="user-form">
      <input type="hidden" id="edit-user-id" />
      <div class="form-group">
        <label class="form-label">Username</label>
        <input class="form-input" type="text" id="f-username" placeholder="username" required />
      </div>
      <div class="form-group" id="pw-group">
        <label class="form-label">Password</label>
        <input class="form-input" type="password" id="f-password" placeholder="minimal 6 karakter" />
      </div>
      <div class="form-group">
        <label class="form-label">Nama Lengkap</label>
        <input class="form-input" type="text" id="f-fullname" placeholder="Nama lengkap (opsional)" />
      </div>
      <div class="form-group">
        <label class="form-label">Email</label>
        <input class="form-input" type="email" id="f-email" placeholder="email@contoh.com (opsional)" />
      </div>
      <div class="form-group">
        <label class="form-label">Role</label>
        <select class="form-select" id="f-role">
          <option value="user">User</option>
          <option value="admin">Admin</option>
        </select>
      </div>
      <div id="active-group" class="form-group" style="display:none">
        <label class="form-label">Status</label>
        <select class="form-select" id="f-active">
          <option value="true">Aktif</option>
          <option value="false">Nonaktif</option>
        </select>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-ghost" onclick="closeModal()">Batal</button>
        <button type="submit" class="btn btn-primary" id="modal-submit">Simpan</button>
      </div>
    </form>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     Modal: Hasil Transkripsi
═══════════════════════════════════════════════ -->
<div class="modal-backdrop" id="modal-result">
  <div class="modal-result">
    <div class="modal-header" style="margin-bottom:14px">
      <span class="modal-title" id="result-modal-title">Hasil Transkripsi</span>
      <button class="modal-close" onclick="closeResultModal()">✕</button>
    </div>
    <div class="result-modal-body">
      <div class="result-meta-grid" id="result-meta-grid"></div>
      <textarea class="result-textarea" id="result-textarea" readonly></textarea>
      <div class="char-info" id="result-char-info"></div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-ghost btn-sm" onclick="copyResult()">📋 Salin</button>
      <button class="btn btn-ghost btn-sm" onclick="downloadResult()">⬇️ Unduh TXT</button>
      <a href="#" id="result-detail-link" class="btn btn-primary btn-sm" target="_blank">
        Buka Halaman Status →
      </a>
    </div>
  </div>
</div>

<!-- ══════════════════════════════════════════════
     Modal: Preview Prompt
═══════════════════════════════════════════════ -->
<div class="modal-overlay" id="prompt-preview-modal"
  onclick="if(event.target===this)closePromptPreview()">
  <div class="modal-box">
    <div class="modal-header">
      <span class="modal-title">Preview Prompt (dengan contoh transkrip)</span>
      <button class="modal-close" onclick="closePromptPreview()">✕</button>
    </div>
    <div class="modal-body">
      <pre id="prompt-preview-content"
        style="white-space:pre-wrap;font-family:'DM Mono',monospace;font-size:.78rem;
               line-height:1.7;color:var(--text);background:var(--bg);padding:16px;
               border-radius:var(--radius-sm);border:1px solid var(--border);"></pre>
    </div>
    <div class="modal-footer">
      <button class="btn btn-ghost btn-sm" onclick="closePromptPreview()">Tutup</button>
    </div>
  </div>
</div>
{% endblock %}

{% block scripts %}
<script>
  // ── Constants & state ──────────────────────────────────────────
  const STATUS_MAP = {
    pending: 'Menunggu', processing: 'Diproses',
    completed: 'Selesai', failed: 'Gagal'
  };
  let editMode           = false;
  let currentTab         = 'stats';
  let currentResultTaskId = null;
  let currentResultText   = '';
  let searchDebounceTimer;
  let deductSearchTimer;

  // Sample transkripsi untuk preview prompt
  const SAMPLE_TRANSCRIPT =
    'HAKIM : Sebutkan nama lengkap saudara terdakwa!\n' +
    'TERDAKWA : Nama saya Budi Santoso, Yang Mulia.\n' +
    'HAKIM : Berapa umur saudara?\n' +
    'TERDAKWA : Saya berumur tiga puluh lima tahun.\n' +
    'JAKSA : Apakah saudara mengenal saksi Ahmad Fauzi?\n' +
    'TERDAKWA : Ya, saya mengenalnya.';

  // ── Helpers ────────────────────────────────────────────────────
  function escapeHtml(str) {
    if (!str) return '';
    return str.replace(/[&<>"']/g, m =>
      ({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;' }[m])
    );
  }

  function fmtToken(n) {
    return (n ?? 0).toLocaleString('id-ID') + ' Token';
  }

  // Estimasi token kasar: 1 token ≈ 3.5 karakter (untuk Bahasa Indonesia)
  function estimateTokens(text) {
    return Math.round((text || '').length / 3.5);
  }

  // ── Toast ──────────────────────────────────────────────────────
  function showAdminToast(message, type = 'info') {
    const existing = document.querySelector('.admin-toast');
    if (existing) existing.remove();
    const toast = document.createElement('div');
    toast.className = 'admin-toast';
    toast.style.borderLeftColor =
      type === 'error'   ? 'var(--danger)'  :
      type === 'success' ? 'var(--success)' :
      type === 'warning' ? 'var(--warning)' : 'var(--accent)';
    toast.textContent = message;
    document.body.appendChild(toast);
    setTimeout(() => toast.remove(), 3500);
  }

  // ── Tab switching ──────────────────────────────────────────────
  function switchTab(name, btn) {
    document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('show'));
    document.querySelectorAll('.admin-tab').forEach(b => b.classList.remove('active'));
    document.getElementById('tab-' + name).classList.add('show');
    btn.classList.add('active');
    currentTab = name;
    if (name === 'stats')     loadStats();
    if (name === 'tasks')     loadTasks();
    if (name === 'users')     loadUsers();
    if (name === 'ba-prompt') loadBAPrompt();
    if (name === 'balance')   loadRates();
    if (name === 'features')  loadFeatures();
    // storage dipanggil dari onclick langsung agar loadStorageStats() & loadStorage() keduanya terpanggil
  }

  // ══════════════════════════════════════════════
  // STATS
  // ══════════════════════════════════════════════
  async function loadStats() {
    try {
      const r = await fetch('/api/admin/stats');
      const d = await r.json();
      document.getElementById('s-total').textContent   = d.tasks.total;
      document.getElementById('s-done').textContent    = d.tasks.completed;
      document.getElementById('s-pending').textContent = d.tasks.pending;
      document.getElementById('s-failed').textContent  = d.tasks.failed;
      document.getElementById('s-users').textContent   = d.users.total;
      document.getElementById('s-users-active').textContent = `${d.users.active} aktif`;
      const dur = d.audio.total_duration_seconds;
      document.getElementById('s-duration').textContent =
        dur >= 3600 ? (dur / 3600).toFixed(1) + ' jam' :
        dur >= 60   ? (dur / 60).toFixed(1)   + ' menit' : dur + ' detik';
    } catch (e) { console.error(e); }
  }

  // ══════════════════════════════════════════════
  // TASKS
  // ══════════════════════════════════════════════
  async function loadTasks() {
    const tbody  = document.getElementById('tasks-tbody');
    const info   = document.getElementById('tasks-info');
    const status = document.getElementById('task-filter').value;
    tbody.innerHTML = '<tr class="empty-row"><td colspan="7">Memuat...</td></tr>';
    try {
      const url = `/api/admin/tasks?limit=100&offset=0${status ? '&status=' + status : ''}`;
      const r = await fetch(url);
      const d = await r.json();
      info.textContent = `Menampilkan ${d.tasks.length} dari ${d.total} task`;
      if (!d.tasks.length) {
        tbody.innerHTML = '<tr class="empty-row"><td colspan="7">Tidak ada data</td></tr>';
        return;
      }
      tbody.innerHTML = d.tasks.map(t => {
        const isCompleted = t.status === 'completed';
        const isFailed    = t.status === 'failed';
        let hasilCol = '<span style="color:var(--muted);font-size:.78rem">—</span>';
        if (isCompleted) {
          const snippet = t.result
            ? t.result.replace(/\n/g, ' ').substring(0, 60) + (t.result.length > 60 ? '…' : '')
            : '(kosong)';
          hasilCol = `<div>
            <button class="btn-lihat-hasil" onclick="openResultModal(${t.id})">🔍 Lihat Hasil</button>
            <span class="result-snippet">${escapeHtml(snippet)}</span>
          </div>`;
        } else if (isFailed) {
          const errSnippet = escapeHtml((t.error_message || 'Tidak diketahui').substring(0, 50));
          hasilCol = `<span style="color:var(--danger);font-size:.78rem">❌ ${errSnippet}</span>`;
        } else {
          hasilCol = `<span style="color:var(--muted);font-size:.78rem">⏳ ${STATUS_MAP[t.status] || t.status}</span>`;
        }
        return `<tr>
          <td>
            <a href="/transkripsi/status/${t.id}"
               style="color:var(--accent);text-decoration:none;font-weight:600">#${t.id}</a>
            ${t.is_court_mode ? '<span class="badge badge-admin" style="margin-left:4px;font-size:.65rem">⚖️</span>' : ''}
           </td>
          <td><span class="badge badge-dot badge-${t.status}">${STATUS_MAP[t.status] || t.status}</span></td>
          <td>
            <code style="font-family:'DM Mono',monospace;font-size:.78rem">${escapeHtml(t.engine)}</code>
            <span style="color:var(--muted);font-size:.75rem;margin-left:4px">${escapeHtml(t.model_type)}</span>
           </td>
          <td>${hasilCol}</td>
          <td style="font-size:.8rem">${t.audio_duration ? t.audio_duration.toFixed(1) + 's' : '—'}</td>
          <td style="font-size:.78rem">${t.created_at ? new Date(t.created_at).toLocaleString('id-ID') : '—'}</td>
          <td><button class="btn btn-danger btn-sm" onclick="deleteTask(${t.id})">Hapus</button></td>
        </tr>`;
      }).join('');
    } catch (e) {
      tbody.innerHTML = '<tr class="empty-row"><td colspan="7" style="color:var(--danger)">Gagal memuat data</td></tr>';
    }
  }

  async function deleteTask(id) {
    if (!confirm(`Hapus task #${id}?`)) return;
    try {
      const r = await fetch(`/api/admin/tasks/${id}`, { method: 'DELETE' });
      if (!r.ok) { const d = await r.json(); throw new Error(d.detail); }
      loadTasks();
    } catch (e) { alert('Gagal: ' + e.message); }
  }

  async function deleteAllTasks() {
    if (!confirm('Hapus semua task yang sudah selesai? Tindakan ini tidak bisa dibatalkan.')) return;
    try {
      await fetch('/api/admin/tasks?status_filter=completed', { method: 'DELETE' });
      loadTasks(); loadStats();
    } catch (e) { alert('Gagal: ' + e.message); }
  }

  // ══════════════════════════════════════════════
  // USERS
  // ══════════════════════════════════════════════
  async function loadUsers() {
    const tbody = document.getElementById('users-tbody');
    tbody.innerHTML = '<tr class="empty-row"><td colspan="9">Memuat...</td></tr>';
    try {
      const r = await fetch('/api/admin/users');
      const d = await r.json();
      if (!d.users.length) {
        tbody.innerHTML = '<tr class="empty-row"><td colspan="9">Belum ada pengguna</td></tr>';
        return;
      }
      tbody.innerHTML = d.users.map(u => {
        const bal    = u.balance ?? 0;
        const balFmt = fmtToken(bal);
        const balCls = bal === 0 ? 'saldo-cell zero' : 'saldo-cell';
        return `<tr>
          <td>#${u.id}</td>
          <td>
            <strong>${escapeHtml(u.username)}</strong>
            ${u.oauth_provider ? `<span style="font-size:.7rem;color:var(--muted)">(${escapeHtml(u.oauth_provider)})</span>` : ''}
           </td>
          <td class="truncate">${escapeHtml(u.full_name || '—')}</td>
          <td class="truncate" style="font-size:.8rem">${escapeHtml(u.email || '—')}</td>
          <td><span class="badge badge-${u.role}">${u.role}</span></td>
          <td>${u.is_active
            ? '<span class="badge badge-completed">Aktif</span>'
            : '<span class="badge badge-failed">Nonaktif</span>'}</td>
          <td class="${balCls}">${balFmt}</td>
          <td style="font-size:.78rem">${u.created_at ? new Date(u.created_at).toLocaleString('id-ID') : '—'}</td>
          <td style="display:flex;gap:6px">
            <button class="btn btn-ghost btn-sm" onclick='openEditUser(${JSON.stringify(u)})'>Edit</button>
            <button class="btn btn-danger btn-sm" onclick="deleteUser(${u.id},'${escapeHtml(u.username)}')">Hapus</button>
           </td>
        </tr>`;
      }).join('');
    } catch (e) {
      tbody.innerHTML = '<tr class="empty-row"><td colspan="9" style="color:var(--danger)">Gagal memuat</td></tr>';
    }
  }

  function openAddUser() {
    editMode = false;
    document.getElementById('modal-title').textContent    = 'Tambah Pengguna';
    document.getElementById('modal-submit').textContent   = 'Tambah';
    document.getElementById('user-form').reset();
    document.getElementById('edit-user-id').value         = '';
    document.getElementById('f-username').disabled        = false;
    document.getElementById('pw-group').style.display     = 'block';
    document.getElementById('active-group').style.display = 'none';
    document.getElementById('f-password').required        = true;
    document.getElementById('modal-user').classList.add('show');
  }

  function openEditUser(u) {
    editMode = true;
    document.getElementById('modal-title').textContent    = `Edit: ${u.username}`;
    document.getElementById('modal-submit').textContent   = 'Simpan';
    document.getElementById('edit-user-id').value         = u.id;
    document.getElementById('f-username').value           = u.username;
    document.getElementById('f-username').disabled        = true;
    document.getElementById('f-fullname').value           = u.full_name || '';
    document.getElementById('f-email').value              = u.email || '';
    document.getElementById('f-role').value               = u.role;
    document.getElementById('f-active').value             = u.is_active ? 'true' : 'false';
    document.getElementById('f-password').value           = '';
    document.getElementById('f-password').required        = false;
    document.getElementById('pw-group').style.display     = 'block';
    document.getElementById('active-group').style.display = 'block';
    document.getElementById('modal-user').classList.add('show');
  }

  function closeModal() { document.getElementById('modal-user').classList.remove('show'); }

  document.getElementById('user-form').addEventListener('submit', async function (e) {
    e.preventDefault();
    const btn = document.getElementById('modal-submit');
    btn.disabled = true; btn.textContent = 'Menyimpan...';
    try {
      if (editMode) {
        const id = document.getElementById('edit-user-id').value;
        const fd = new FormData();
        const pw = document.getElementById('f-password').value.trim();
        fd.append('full_name',  document.getElementById('f-fullname').value);
        fd.append('email',      document.getElementById('f-email').value);
        fd.append('role',       document.getElementById('f-role').value);
        fd.append('is_active',  document.getElementById('f-active').value);
        if (pw) fd.append('new_password', pw);
        const r = await fetch(`/api/admin/users/${id}`, { method: 'PATCH', body: fd });
        if (!r.ok) { const d = await r.json(); throw new Error(d.detail); }
      } else {
        const fd = new FormData();
        fd.append('username',  document.getElementById('f-username').value);
        fd.append('password',  document.getElementById('f-password').value);
        fd.append('full_name', document.getElementById('f-fullname').value);
        fd.append('email',     document.getElementById('f-email').value);
        fd.append('role',      document.getElementById('f-role').value);
        const r = await fetch('/api/admin/users', { method: 'POST', body: fd });
        if (!r.ok) { const d = await r.json(); throw new Error(d.detail); }
      }
      closeModal(); loadUsers();
    } catch (e) { alert('Gagal: ' + e.message); }
    finally { btn.disabled = false; btn.textContent = editMode ? 'Simpan' : 'Tambah'; }
  });

  async function deleteUser(id, username) {
    if (!confirm(`Hapus pengguna "${username}"?`)) return;
    try {
      const r = await fetch(`/api/admin/users/${id}`, { method: 'DELETE' });
      if (!r.ok) { const d = await r.json(); throw new Error(d.detail); }
      loadUsers();
    } catch (e) { alert('Gagal: ' + e.message); }
  }

  // ══════════════════════════════════════════════
  // RESULT MODAL
  // ══════════════════════════════════════════════
  async function openResultModal(taskId) {
    currentResultTaskId = taskId;
    document.getElementById('result-modal-title').textContent = `Hasil Transkripsi — Task #${taskId}`;
    document.getElementById('result-detail-link').href        = `/transkripsi/status/${taskId}`;
    document.getElementById('result-textarea').value          = 'Memuat hasil...';
    document.getElementById('result-meta-grid').innerHTML     = '';
    document.getElementById('result-char-info').textContent   = '';
    document.getElementById('modal-result').classList.add('show');
    try {
      const rs = await fetch(`/api/status/${taskId}`);
      if (!rs.ok) throw new Error('Gagal memuat');
      const d   = await rs.json();
      const txt = d.result || '';
      currentResultText = txt;
      const metas = [
        { k: 'Engine',       v: d.engine || '—' },
        { k: 'Model',        v: d.model_type || '—' },
        { k: 'Durasi Audio', v: d.audio_duration  ? d.audio_duration.toFixed(1)  + 's' : '—' },
        { k: 'Waktu Proses', v: d.processing_time ? d.processing_time.toFixed(1) + 's' : '—' },
        { k: 'Mode Sidang',  v: d.is_court_mode ? '✅ Aktif' : '—' },
        { k: 'Dibuat',       v: d.created_at ? new Date(d.created_at).toLocaleString('id-ID') : '—' },
      ];
      document.getElementById('result-meta-grid').innerHTML = metas.map(m =>
        `<div class="result-meta-item">
          <div class="result-meta-key">${m.k}</div>
          <div class="result-meta-val">${m.v}</div>
        </div>`
      ).join('');
      document.getElementById('result-textarea').value = txt || '(Hasil transkripsi kosong)';
      const wc = txt.trim() ? txt.trim().split(/\s+/).filter(Boolean).length : 0;
      document.getElementById('result-char-info').textContent =
        `${txt.length.toLocaleString()} karakter · ${wc.toLocaleString()} kata`;
    } catch (e) {
      document.getElementById('result-textarea').value =
        'Gagal memuat hasil transkripsi. Coba buka halaman status langsung.';
    }
  }

  function closeResultModal() {
    document.getElementById('modal-result').classList.remove('show');
    currentResultTaskId = null; currentResultText = '';
  }

  function copyResult() {
    navigator.clipboard.writeText(currentResultText).then(() => {
      const btn  = event.target.closest('button');
      const orig = btn.innerHTML;
      btn.innerHTML = '✅ Tersalin!';
      setTimeout(() => btn.innerHTML = orig, 2000);
    });
  }

  function downloadResult() {
    const blob = new Blob([currentResultText], { type: 'text/plain' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href     = url;
    a.download = `transkripsi-task-${currentResultTaskId}.txt`;
    a.click();
    URL.revokeObjectURL(url);
  }

  // ══════════════════════════════════════════════
  // BA PROMPT
  // ══════════════════════════════════════════════

  function _applyPromptStats(promptText, isDefault, updatedAt) {
    const charLen  = (promptText || '').length;
    const tokenEst = estimateTokens(promptText);

    document.getElementById('pstat-chars').textContent = charLen.toLocaleString('id-ID');
    document.getElementById('pstat-token').textContent = '~' + tokenEst;

    if (isDefault) {
      document.getElementById('pstat-updated').textContent     = 'Bawaan sistem';
      document.getElementById('pstat-updated-sub').textContent = 'belum dimodifikasi';
    } else {
      document.getElementById('pstat-updated').textContent     =
        updatedAt ? new Date(updatedAt).toLocaleDateString('id-ID') : '—';
      document.getElementById('pstat-updated-sub').textContent = 'prompt kustom aktif';
    }

    const badge = document.getElementById('prompt-status-badge');
    if (isDefault) {
      badge.textContent = '● Default';
      badge.className   = 'prompt-status-badge is-default';
    } else {
      badge.textContent = '● Custom (tersimpan)';
      badge.className   = 'prompt-status-badge is-custom';
    }

    const hasPlaceholder = (promptText || '').includes('{transcript}');
    const alertEl = document.getElementById('prompt-ph-alert');
    const msgEl   = document.getElementById('prompt-ph-msg');
    if (hasPlaceholder) {
      alertEl.className   = 'alert alert-info';
      msgEl.textContent   = '✓ Placeholder {transcript} ditemukan — prompt siap digunakan.';
    } else {
      alertEl.className   = 'alert alert-danger';
      msgEl.textContent   = '⚠️ Placeholder {transcript} tidak ditemukan! Transkrip tidak akan disisipkan.';
    }

    const tokenBadge = document.getElementById('prompt-token-badge');
    tokenBadge.style.display = 'inline-flex';
    if (tokenEst < 200) {
      tokenBadge.className   = 'token-badge ok';
      tokenBadge.textContent = `~${tokenEst} token — Hemat`;
    } else if (tokenEst < 350) {
      tokenBadge.className   = 'token-badge warn';
      tokenBadge.textContent = `~${tokenEst} token — Sedang`;
    } else {
      tokenBadge.className   = 'token-badge bad';
      tokenBadge.textContent = `~${tokenEst} token — Boros`;
    }
  }

  function updatePromptCharCount() {
    const ta  = document.getElementById('ba-prompt-textarea');
    const el  = document.getElementById('prompt-char-count');
    if (!ta || !el) return;

    const text           = ta.value;
    const len            = text.length;
    const hasPlaceholder = text.includes('{transcript}');
    const tokenEst       = estimateTokens(text);

    el.innerHTML =
      `${len.toLocaleString('id-ID')} karakter · ` +
      (hasPlaceholder
        ? '<span style="color:var(--success)">✅ {transcript} ada</span>'
        : '<span style="color:var(--danger)">⚠️ {transcript} tidak ada</span>');

    const tokenBadge = document.getElementById('prompt-token-badge');
    tokenBadge.style.display = 'inline-flex';
    if (tokenEst < 200) {
      tokenBadge.className   = 'token-badge ok';
      tokenBadge.textContent = `~${tokenEst} token — Hemat`;
    } else if (tokenEst < 350) {
      tokenBadge.className   = 'token-badge warn';
      tokenBadge.textContent = `~${tokenEst} token — Sedang`;
    } else {
      tokenBadge.className   = 'token-badge bad';
      tokenBadge.textContent = `~${tokenEst} token — Boros`;
    }

    const alertEl = document.getElementById('prompt-ph-alert');
    const msgEl   = document.getElementById('prompt-ph-msg');
    if (hasPlaceholder) {
      alertEl.className = 'alert alert-info';
      msgEl.textContent = '✓ Placeholder {transcript} ditemukan — prompt siap digunakan.';
    } else {
      alertEl.className = 'alert alert-danger';
      msgEl.textContent = '⚠️ Placeholder {transcript} tidak ditemukan! Transkrip tidak akan disisipkan.';
    }
  }

  async function loadBAPrompt() {
    try {
      const r = await fetch('/api/admin/ba-prompt');
      if (!r.ok) return;
      const d = await r.json();

      document.getElementById('ba-prompt-textarea').value = d.prompt_text || '';

      const updatedEl = document.getElementById('prompt-updated-at');
      if (d.is_default) {
        updatedEl.textContent = '(menggunakan prompt default)';
      } else if (d.updated_at) {
        updatedEl.textContent = 'Diperbarui: ' + new Date(d.updated_at).toLocaleString('id-ID');
      } else {
        updatedEl.textContent = '';
      }

      _applyPromptStats(d.prompt_text || '', d.is_default, d.updated_at);
      updatePromptCharCount();
    } catch (e) { console.warn('Load prompt error:', e); }
  }

  async function loadDefaultPrompt() {
    if (!confirm('Reset ke prompt default bawaan? Perubahan yang belum disimpan akan hilang.')) return;
    try {
      const r = await fetch('/api/admin/ba-prompt/default');
      if (!r.ok) throw new Error('Gagal memuat');
      const d = await r.json();
      document.getElementById('ba-prompt-textarea').value = d.prompt_text;
      document.getElementById('prompt-updated-at').textContent = '(prompt default — belum disimpan)';
      _applyPromptStats(d.prompt_text, true, null);
      updatePromptCharCount();
      showAdminToast('Prompt direset ke default. Klik Simpan untuk menerapkan.', 'warning');
    } catch (e) { showAdminToast('Gagal memuat prompt default.', 'error'); }
  }

  async function saveBAPrompt() {
    const btn  = document.getElementById('btn-save-ba-prompt');
    const text = document.getElementById('ba-prompt-textarea').value.trim();

    if (!text) {
      showAdminToast('Prompt tidak boleh kosong.', 'error'); return;
    }
    if (!text.includes('{transcript}') &&
        !confirm('Prompt tidak mengandung {transcript}. Transkrip tidak akan disisipkan. Tetap simpan?')) {
      return;
    }

    btn.disabled = true;
    btn.innerHTML = '⏳ Menyimpan...';
    try {
      const r = await fetch('/api/admin/ba-prompt', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ prompt_text: text }),
      });
      const d = await r.json();
      if (!r.ok) throw new Error(d.detail || 'Gagal menyimpan.');

      const updatedEl = document.getElementById('prompt-updated-at');
      updatedEl.textContent = d.updated_at
        ? 'Diperbarui: ' + new Date(d.updated_at).toLocaleString('id-ID')
        : '';

      _applyPromptStats(text, false, d.updated_at);
      showAdminToast('✅ Prompt berhasil disimpan!', 'success');
    } catch (e) { showAdminToast('Gagal: ' + e.message, 'error'); }
    finally {
      btn.disabled = false;
      btn.innerHTML =
        `<svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
          <path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v14a2 2 0 01-2 2z"/>
          <path d="M17 21v-8H7v8M7 3v5h8"/>
        </svg> Simpan Prompt`;
    }
  }

  function previewPrompt() {
    const template = document.getElementById('ba-prompt-textarea').value;
    if (!template.trim()) {
      showAdminToast('Prompt masih kosong.', 'error'); return;
    }
    document.getElementById('prompt-preview-content').textContent =
      template.replace('{transcript}', SAMPLE_TRANSCRIPT);
    document.getElementById('prompt-preview-modal').classList.add('show');
  }

  function closePromptPreview() {
    document.getElementById('prompt-preview-modal').classList.remove('show');
  }

  // ══════════════════════════════════════════════
  // USER SEARCH — Top-up
  // ══════════════════════════════════════════════
  async function searchUsers(query) {
    if (!query.trim()) {
      document.getElementById('user_search_results').style.display = 'none'; return;
    }
    try {
      const res  = await fetch(`/api/admin/users?limit=10&search=${encodeURIComponent(query)}`);
      const data = await res.json();
      const div  = document.getElementById('user_search_results');
      div.innerHTML = (data.users && data.users.length)
        ? data.users.map(u =>
            `<div onclick="selectUser(${u.id},'${escapeHtml(u.username)}','${escapeHtml(u.full_name || u.username)}',${u.balance ?? 0})">
              <strong>${escapeHtml(u.username)}</strong>
              ${u.full_name ? `(${escapeHtml(u.full_name)})` : ''}
              — Saldo: ${fmtToken(u.balance)}
            </div>`
          ).join('')
        : '<div style="padding:8px;color:var(--muted);">Tidak ditemukan</div>';
      div.style.display = 'block';
    } catch (e) { console.error('Search error:', e); }
  }

  function selectUser(id, username, fullName, balance) {
    document.getElementById('selected_user_id').value              = id;
    document.getElementById('selected_user_name').innerText        = `${username} (${fullName})`;
    document.getElementById('selected_user_id_display').innerText  = id;
    document.getElementById('selected_user_balance').innerText     = fmtToken(balance);
    document.getElementById('selected_user_display').style.display = 'flex';
    document.getElementById('search_user_input').value             = `${username} (${fullName})`;
    document.getElementById('user_search_results').style.display   = 'none';
  }

  function clearSelectedUser() {
    document.getElementById('selected_user_id').value              = '';
    document.getElementById('selected_user_display').style.display = 'none';
    document.getElementById('search_user_input').value             = '';
  }

  document.getElementById('search_user_input')?.addEventListener('input', function (e) {
    clearTimeout(searchDebounceTimer);
    const query = e.target.value;
    if (!query) {
      document.getElementById('user_search_results').style.display   = 'none';
      document.getElementById('selected_user_display').style.display = 'none';
      document.getElementById('selected_user_id').value              = '';
      return;
    }
    searchDebounceTimer = setTimeout(() => searchUsers(query), 300);
  });

  // ══════════════════════════════════════════════
  // USER SEARCH — Deduct
  // ══════════════════════════════════════════════
  async function searchDeductUsers(query) {
    if (!query.trim()) {
      document.getElementById('deduct_user_search_results').style.display = 'none'; return;
    }
    try {
      const res  = await fetch(`/api/admin/users?limit=10&search=${encodeURIComponent(query)}`);
      const data = await res.json();
      const div  = document.getElementById('deduct_user_search_results');
      div.innerHTML = (data.users && data.users.length)
        ? data.users.map(u =>
            `<div onclick="selectDeductUser(${u.id},'${escapeHtml(u.username)}','${escapeHtml(u.full_name || u.username)}',${u.balance ?? 0})">
              <strong>${escapeHtml(u.username)}</strong>
              ${u.full_name ? `(${escapeHtml(u.full_name)})` : ''}
              — Saldo: ${fmtToken(u.balance)}
            </div>`
          ).join('')
        : '<div style="padding:8px;color:var(--muted);">Tidak ditemukan</div>';
      div.style.display = 'block';
    } catch (e) { console.error('Deduct search error:', e); }
  }

  function selectDeductUser(id, username, fullName, balance) {
    document.getElementById('deduct_selected_user_id').value              = id;
    document.getElementById('deduct_selected_user_name').innerText        = `${username} (${fullName})`;
    document.getElementById('deduct_selected_user_id_display').innerText  = id;
    document.getElementById('deduct_selected_user_balance').innerText     = fmtToken(balance);
    document.getElementById('deduct_selected_user_display').style.display = 'flex';
    document.getElementById('deduct_search_user_input').value             = `${username} (${fullName})`;
    document.getElementById('deduct_user_search_results').style.display   = 'none';
  }

  function clearDeductSelectedUser() {
    document.getElementById('deduct_selected_user_id').value              = '';
    document.getElementById('deduct_selected_user_display').style.display = 'none';
    document.getElementById('deduct_search_user_input').value             = '';
  }

  document.getElementById('deduct_search_user_input')?.addEventListener('input', function (e) {
    clearTimeout(deductSearchTimer);
    const query = e.target.value;
    if (!query) {
      document.getElementById('deduct_user_search_results').style.display   = 'none';
      document.getElementById('deduct_selected_user_display').style.display = 'none';
      document.getElementById('deduct_selected_user_id').value              = '';
      return;
    }
    deductSearchTimer = setTimeout(() => searchDeductUsers(query), 300);
  });

  // Tutup dropdown saat klik di luar
  document.addEventListener('click', function (e) {
    const topupContainer = document.getElementById('topup-search-container');
    const topupResults   = document.getElementById('user_search_results');
    if (topupContainer && topupResults && !topupContainer.contains(e.target))
      topupResults.style.display = 'none';

    const deductContainer = document.getElementById('deduct-search-container');
    const deductResults   = document.getElementById('deduct_user_search_results');
    if (deductContainer && deductResults && !deductContainer.contains(e.target))
      deductResults.style.display = 'none';
  });

  // ══════════════════════════════════════════════
  // BALANCE
  // ══════════════════════════════════════════════
  async function addBalance() {
    const userId      = document.getElementById('selected_user_id').value;
    const amount      = document.getElementById('topup_amount').value;
    const description = document.getElementById('topup_desc').value;
    if (!userId) { showAdminToast('Pilih pengguna terlebih dahulu.', 'error'); return; }
    if (!amount || parseInt(amount) <= 0) { showAdminToast('Isi jumlah yang valid (minimal 1).', 'error'); return; }
    try {
      const res  = await fetch(`/api/admin/users/${userId}/balance`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount: parseInt(amount), description: description || null }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.detail || 'Gagal menambah saldo.');
      showAdminToast(`Saldo berhasil ditambahkan. Saldo baru: ${fmtToken(data.new_balance)}`, 'success');
      clearSelectedUser();
      document.getElementById('topup_amount').value = '';
      document.getElementById('topup_desc').value   = '';
    } catch (e) { showAdminToast(e.message, 'error'); }
  }

  async function deductBalance() {
    const userId      = document.getElementById('deduct_selected_user_id').value;
    const amount      = document.getElementById('deduct_amount').value;
    const description = document.getElementById('deduct_desc').value;
    if (!userId) { showAdminToast('Pilih pengguna terlebih dahulu.', 'error'); return; }
    if (!amount || parseInt(amount) <= 0) { showAdminToast('Isi jumlah yang valid (minimal 1).', 'error'); return; }

    const saldoEl = document.getElementById('deduct_selected_user_balance');
    const namaEl  = document.getElementById('deduct_selected_user_name');
    const konfirm =
      `Kurangi saldo ${namaEl.innerText} sebesar ${fmtToken(parseInt(amount))}?\n` +
      `Saldo saat ini: ${saldoEl.innerText}\nTindakan ini tidak dapat dibatalkan.`;
    if (!confirm(konfirm)) return;

    try {
      const res  = await fetch(`/api/admin/users/${userId}/balance/deduct`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount: parseInt(amount), description: description || null }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.detail || 'Gagal mengurangi saldo.');
      showAdminToast(`Saldo berhasil dikurangi. Saldo baru: ${fmtToken(data.new_balance)}`, 'success');
      clearDeductSelectedUser();
      document.getElementById('deduct_amount').value = '';
      document.getElementById('deduct_desc').value   = '';
    } catch (e) { showAdminToast(e.message, 'error'); }
  }

  // ══════════════════════════════════════════════
  // RATES
  // ══════════════════════════════════════════════
  async function loadRates() {
    try {
      const res  = await fetch('/api/admin/rates');
      if (!res.ok) throw new Error('Gagal load tarif');
      const data = await res.json();
      document.getElementById('rate_transcription').value = data.transcription_rate_per_minute;
      document.getElementById('rate_ba').value            = data.ba_rate;
    } catch (e) { console.warn(e); }
  }

  async function updateRates() {
    const rateTrans = document.getElementById('rate_transcription').value;
    const rateBa    = document.getElementById('rate_ba').value;
    if (!rateTrans || !rateBa || rateTrans <= 0 || rateBa <= 0) {
      showAdminToast('Tarif harus positif.', 'error'); return;
    }
    try {
      const res = await fetch('/api/admin/rates', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          transcription_rate_per_minute: parseInt(rateTrans),
          ba_rate: parseInt(rateBa),
        }),
      });
      if (!res.ok) throw new Error('Gagal simpan tarif');
      showAdminToast('Tarif berhasil disimpan.', 'success');
    } catch (e) { showAdminToast(e.message, 'error'); }
  }

  // ══════════════════════════════════════════════
  // FEATURE FLAGS
  // ══════════════════════════════════════════════

  function _renderFeatureCard(f) {
    const on = f.is_enabled;
    return `
      <div class="feature-card ${on ? '' : 'disabled-card'}" id="fcard-${f.key}">
        <div class="feature-icon">${f.icon || '⚙️'}</div>
        <div class="feature-info">
          <div class="feature-label">${escapeHtml(f.label)}</div>
          <div class="feature-desc">${escapeHtml(f.description || '')}</div>
          <span class="feature-status-badge ${on ? 'on' : 'off'}" id="fbadge-${f.key}">
            ${on ? '✅ Aktif' : '🔴 Nonaktif — Maintenance'}
          </span>
        </div>
        <div class="toggle-wrap">
          <span class="toggle-label-small" id="ftoggle-lbl-${f.key}">${on ? 'ON' : 'OFF'}</span>
          <label
            class="toggle-switch"
            id="toggle-${f.key}"
            title="${on ? 'Klik untuk nonaktifkan' : 'Klik untuk aktifkan'}"
          >
            <input
              type="checkbox"
              ${on ? 'checked' : ''}
              onchange="toggleFeature('${escapeHtml(f.key)}', this.checked, this)"
            />
            <span class="toggle-track"></span>
          </label>
        </div>
      </div>
    `;
  }

  async function loadFeatures() {
    const grid = document.getElementById('features-grid');
    const info = document.getElementById('features-last-refresh');
    grid.innerHTML = `
      <div class="stat-card" style="grid-column:1/-1;text-align:center;padding:40px;color:var(--muted)">
        Memuat fitur...
      </div>`;
    try {
      const r = await fetch('/api/admin/features');
      if (!r.ok) throw new Error('Gagal memuat feature flags');
      const d = await r.json();
      const features = d.features;
      if (!features || !features.length) {
        grid.innerHTML = `
          <div class="stat-card" style="grid-column:1/-1;text-align:center;padding:40px;color:var(--muted)">
            Tidak ada fitur terdaftar.
          </div>`;
        return;
      }
      grid.innerHTML = features.map(f => _renderFeatureCard(f)).join('');
      info.textContent = 'Terakhir dimuat: ' + new Date().toLocaleString('id-ID');
    } catch (e) {
      grid.innerHTML = `
        <div class="stat-card" style="grid-column:1/-1;text-align:center;padding:40px;color:var(--danger)">
          Gagal memuat daftar fitur. Coba refresh.
        </div>`;
      console.error('[loadFeatures]', e);
    }
  }

  async function toggleFeature(key, enabled, checkbox) {
    const switchEl = document.getElementById(`toggle-${key}`);
    const card     = document.getElementById(`fcard-${key}`);
    const badge    = document.getElementById(`fbadge-${key}`);
    const lbl      = document.getElementById(`ftoggle-lbl-${key}`);

    if (switchEl) switchEl.classList.add('busy');
    if (checkbox) checkbox.disabled = true;

    try {
      const r = await fetch(
        `/api/admin/features/${encodeURIComponent(key)}?enabled=${enabled}`,
        { method: 'PATCH' }
      );
      const d = await r.json();
      if (!r.ok) throw new Error(d.detail || 'Gagal mengubah status fitur.');

      if (card)  card.classList.toggle('disabled-card', !enabled);
      if (badge) {
        badge.className   = `feature-status-badge ${enabled ? 'on' : 'off'}`;
        badge.textContent = enabled ? '✅ Aktif' : '🔴 Nonaktif — Maintenance';
      }
      if (lbl)      lbl.textContent = enabled ? 'ON' : 'OFF';
      if (switchEl) switchEl.title  = enabled
        ? 'Klik untuk nonaktifkan'
        : 'Klik untuk aktifkan';

      showAdminToast(d.message, 'success');
    } catch (e) {
      if (checkbox) checkbox.checked = !enabled;
      if (card)     card.classList.toggle('disabled-card', enabled);
      showAdminToast('Gagal: ' + e.message, 'error');
    } finally {
      if (switchEl) switchEl.classList.remove('busy');
      if (checkbox) checkbox.disabled = false;
    }
  }

  // ══════════════════════════════════════════════
  // STORAGE TAB
  // ══════════════════════════════════════════════
  let selectedStorageTaskIds = new Set();

  async function loadStorageStats() {
    try {
      const r = await fetch('/api/admin/storage/stats');
      if (!r.ok) return;
      const d = await r.json();
      document.getElementById('st-total-files').textContent = d.total_files.toLocaleString('id-ID');
      document.getElementById('st-total-mb').textContent    = d.total_mb.toLocaleString('id-ID', {minimumFractionDigits:1});
      document.getElementById('st-orphan-files').textContent= d.orphan_files.toLocaleString('id-ID');
      document.getElementById('st-orphan-mb').textContent   =
        d.orphan_files > 0 ? `${d.orphan_mb} MB tidak terpakai` : 'tidak ada orphan';
    } catch(e) { console.warn('[storage stats]', e); }
  }

  async function loadStorage() {
    const statusVal = document.getElementById('storage-filter-status').value;
    const fileVal   = document.getElementById('storage-filter-file').value;

    let url = `/api/admin/storage?limit=100&offset=0`;
    if (statusVal) url += `&status=${statusVal}`;
    if (fileVal === 'exists')  url += `&has_file=true`;
    if (fileVal === 'missing') url += `&has_file=false`;

    const tbody = document.getElementById('storage-tbody');
    const info  = document.getElementById('storage-info');
    tbody.innerHTML = `<tr class="empty-row"><td colspan="8">Memuat...</td></tr>`;

    try {
      const r = await fetch(url);
      if (!r.ok) throw new Error('Gagal memuat data storage');
      const d = await r.json();

      if (!d.records.length) {
        tbody.innerHTML = `<tr class="empty-row"><td colspan="8">Tidak ada data.</td></tr>`;
        info.textContent = '';
        return;
      }

      selectedStorageTaskIds.clear();
      updateBulkBar();
      document.getElementById('storage-select-all').checked = false;

      tbody.innerHTML = d.records.map(rec => {
        const fileLabel = rec.file_exists
          ? `<span class="file-size-badge exists">✓ Ada</span>`
          : `<span class="file-size-badge missing">✗ Hilang</span>`;
        const sizeTxt = rec.file_exists
          ? `<span class="file-size-badge exists">${(rec.file_size_mb).toFixed(2)} MB</span>`
          : `<span style="color:var(--muted);font-size:.76rem">—</span>`;
        const dur = rec.audio_duration ? `${Math.floor(rec.audio_duration/60)}m ${Math.round(rec.audio_duration%60)}s` : '—';
        const tgl = rec.created_at ? new Date(rec.created_at).toLocaleDateString('id-ID',{day:'2-digit',month:'short',year:'numeric'}) : '—';
        const fileName = rec.file_name
          ? `<span class="truncate" title="${rec.file_name}" style="max-width:140px;display:block;font-family:'DM Mono',monospace;font-size:.72rem;color:var(--muted)">${rec.file_name}</span>`
          : '<span style="color:var(--muted);font-size:.76rem">—</span>';

        return `
          <tr class="storage-row" id="storage-row-${rec.task_id}">
            <td><input type="checkbox" class="storage-row-check"
                 onchange="toggleStorageRow(${rec.task_id}, this.checked)"
                 id="storage-check-${rec.task_id}"></td>
            <td>
              <a href="/transkripsi/status/${rec.task_id}" target="_blank"
                 style="color:var(--accent);font-weight:600;font-size:.84rem">#${rec.task_id}</a>
              ${rec.user_id ? `<span style="color:var(--muted);font-size:.72rem"> · user ${rec.user_id}</span>` : ''}
            </td>
            <td><span class="badge badge-${rec.status}" style="font-size:.72rem">${rec.status}</span></td>
            <td>${fileLabel}${fileName}</td>
            <td>${sizeTxt}</td>
            <td style="font-size:.82rem;color:var(--muted)">${dur}</td>
            <td style="font-size:.78rem;color:var(--muted)">${tgl}</td>
            <td>
              <div style="display:flex;gap:4px;flex-wrap:wrap">
                ${rec.file_exists ? `
                  <button class="btn btn-ghost btn-sm" style="font-size:.72rem;padding:3px 8px"
                          onclick="deleteStorageFile(${rec.task_id}, false)"
                          title="Hapus file audio saja, record task dipertahankan">
                    🗑 File
                  </button>
                ` : ''}
                <button class="btn btn-danger btn-sm" style="font-size:.72rem;padding:3px 8px"
                        onclick="deleteStorageFile(${rec.task_id}, true)"
                        ${rec.status==='processing'?'disabled':''}>
                  ❌ Task
                </button>
              </div>
            </td>
          </tr>
        `;
      }).join('');

      info.textContent = `Menampilkan ${d.records.length} dari ${d.total} record.`;

    } catch(e) {
      tbody.innerHTML = `<tr class="empty-row"><td colspan="8" style="color:var(--danger)">Gagal memuat: ${e.message}</td></tr>`;
    }
  }

  function toggleStorageRow(taskId, checked) {
    if (checked) selectedStorageTaskIds.add(taskId);
    else         selectedStorageTaskIds.delete(taskId);
    updateBulkBar();
  }

  function toggleSelectAllStorage(checked) {
    document.querySelectorAll('.storage-row-check').forEach(cb => {
      cb.checked = checked;
      const id = parseInt(cb.id.replace('storage-check-', ''));
      if (checked) selectedStorageTaskIds.add(id);
      else         selectedStorageTaskIds.delete(id);
    });
    updateBulkBar();
  }

  function clearStorageSelection() {
    selectedStorageTaskIds.clear();
    document.querySelectorAll('.storage-row-check').forEach(cb => cb.checked = false);
    document.getElementById('storage-select-all').checked = false;
    updateBulkBar();
  }

  function updateBulkBar() {
    const bar = document.getElementById('bulk-action-bar');
    const lbl = document.getElementById('bulk-count-label');
    const n = selectedStorageTaskIds.size;
    if (n > 0) {
      bar.classList.add('show');
      lbl.textContent = `${n} task dipilih`;
    } else {
      bar.classList.remove('show');
    }
  }

  async function deleteStorageFile(taskId, deleteTask) {
    const what = deleteTask
      ? 'file audio DAN record task dari database'
      : 'file audio saja (record task dipertahankan)';
    if (!confirm(`Hapus ${what} untuk Task #${taskId}?\nTindakan ini tidak bisa dibatalkan.`)) return;

    try {
      const r = await fetch(
        `/api/admin/storage/file/${taskId}?delete_task=${deleteTask}`,
        { method: 'DELETE' }
      );
      const d = await r.json();
      if (!r.ok) throw new Error(d.detail || 'Gagal menghapus.');
      showAdminToast(d.message, 'success');
      loadStorage();
      loadStorageStats();
    } catch(e) { showAdminToast('❌ ' + e.message, 'error'); }
  }

  async function bulkDeleteFiles(deleteTasks) {
    const ids = [...selectedStorageTaskIds];
    if (!ids.length) return;
    const what = deleteTasks ? 'file + record task' : 'file audio saja';
    if (!confirm(`Hapus ${what} untuk ${ids.length} task yang dipilih?\nTindakan ini tidak bisa dibatalkan.`)) return;

    try {
      const r = await fetch(
        `/api/admin/storage/bulk?delete_tasks=${deleteTasks}`,
        {
          method: 'DELETE',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ task_ids: ids }),
        }
      );
      const d = await r.json();
      if (!r.ok) throw new Error(d.detail || 'Gagal bulk delete.');
      showAdminToast(`✅ ${d.message}`, 'success');
      clearStorageSelection();
      loadStorage();
      loadStorageStats();
    } catch(e) { showAdminToast('❌ ' + e.message, 'error'); }
  }

  async function previewOrphans() {
    try {
      const r = await fetch('/api/admin/storage/stats');
      const d = await r.json();
      if (d.orphan_files === 0) {
        showAdminToast('✅ Tidak ada file orphan di direktori upload.', 'success');
      } else {
        showAdminToast(
          `📁 Ditemukan ${d.orphan_files} file orphan (${d.orphan_mb} MB). Klik "Hapus Orphan" untuk membersihkan.`,
          'error'
        );
      }
    } catch(e) { showAdminToast('Gagal: ' + e.message, 'error'); }
  }

  async function deleteOrphans() {
    if (!confirm('Hapus semua file orphan (file di disk yang tidak ada record task-nya di database)?\n\nTindakan ini PERMANEN dan tidak bisa dibatalkan.')) return;
    try {
      const r = await fetch('/api/admin/storage/orphans', { method: 'DELETE' });
      const d = await r.json();
      if (!r.ok) throw new Error(d.detail || 'Gagal.');
      showAdminToast(`✅ ${d.message}`, 'success');
      loadStorage();
      loadStorageStats();
    } catch(e) { showAdminToast('❌ ' + e.message, 'error'); }
  }

  async function deleteAllCompletedFiles() {
    if (!confirm('Hapus SEMUA file audio task yang berstatus "selesai"?\n\nRecord task di database dipertahankan, hanya file di disk yang dihapus.\n\nTidak bisa dibatalkan!')) return;
    try {
      const r = await fetch('/api/admin/storage?limit=200&status=completed&has_file=true');
      const d = await r.json();
      const ids = d.records.map(rec => rec.task_id);
      if (!ids.length) { showAdminToast('Tidak ada file yang perlu dihapus.', 'success'); return; }

      const r2 = await fetch('/api/admin/storage/bulk?delete_tasks=false', {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ task_ids: ids }),
      });
      const d2 = await r2.json();
      if (!r2.ok) throw new Error(d2.detail || 'Gagal.');
      showAdminToast(`✅ ${d2.message}`, 'success');
      loadStorage();
      loadStorageStats();
    } catch(e) { showAdminToast('❌ ' + e.message, 'error'); }
  }

  async function deleteAllFiles() {
    const txt = 'HAPUS SEMUA';
    const input = prompt(
      `⚠️ PERINGATAN KERAS ⚠️\n\nIni akan menghapus SEMUA file audio dari disk untuk semua task.\n\nKetik "${txt}" untuk konfirmasi:`
    );
    if (input !== txt) { showAdminToast('Dibatalkan — konfirmasi tidak cocok.', 'error'); return; }

    try {
      const r = await fetch('/api/admin/storage?limit=500&has_file=true');
      const d = await r.json();
      const ids = d.records.map(rec => rec.task_id);
      if (!ids.length) { showAdminToast('Tidak ada file yang perlu dihapus.', 'success'); return; }

      const r2 = await fetch('/api/admin/storage/bulk?delete_tasks=false', {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ task_ids: ids }),
      });
      const d2 = await r2.json();
      if (!r2.ok) throw new Error(d2.detail || 'Gagal.');
      showAdminToast(`✅ ${d2.message} (${d2.files_deleted} file)`, 'success');
      loadStorage();
      loadStorageStats();
    } catch(e) { showAdminToast('❌ ' + e.message, 'error'); }
  }

  // ══════════════════════════════════════════════
  // Event listeners & Init
  // ══════════════════════════════════════════════
  document.getElementById('ba-prompt-textarea')
    ?.addEventListener('input', updatePromptCharCount);

  document.getElementById('modal-user')
    .addEventListener('click', function (e) { if (e.target === this) closeModal(); });

  document.getElementById('modal-result')
    .addEventListener('click', function (e) { if (e.target === this) closeResultModal(); });

  // Inisialisasi saat halaman dimuat
  loadStats();
</script>
{% endblock %}