<?php
// ─────────────────────────────────────────────────────────────
//  admin/submissions.php — Submissions Inbox  (P4)
//  Standalone full-screen page with slide-in detail panel,
//  search, filter, CSV export, PDF download, delete.
// ─────────────────────────────────────────────────────────────
require_once dirname(__DIR__) . '/lib.php';
session_start_safe();
if (!admin_logged_in()) {
    header('Location: ../admin.php');
    exit;
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/>
<title>Submissions — FormBuilder</title>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@400;600;700;800&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600&display=swap" rel="stylesheet"/>
<style>
/* ── Reset & tokens ─────────────────────────────────────────── */
*{box-sizing:border-box;margin:0;padding:0}
:root{
  --bg:#f4f5f7;
  --surface:#ffffff;
  --card:#ffffff;
  --card2:#f8f9fb;
  --border:#e8eaed;
  --border2:#d1d5db;
  --accent:#e09500;
  --accent-d:#b87800;
  --accent-pale:#fff8eb;
  --accent-glow:rgba(224,149,0,0.15);
  --text:#111827;
  --muted:#9ca3af;
  --muted2:#4b5563;
  --green:#16a34a;
  --green-pale:#f0fdf4;
  --red:#dc2626;
  --red-pale:#fef2f2;
  --yellow:#d97706;
  --yellow-pale:#fffbeb;
  --blue:#e09500;
  --blue-pale:#fff8eb;
  --topbar:58px;
  --panel:420px;
  --sidebar:220px;
  --r:10px;
  --font-display:'Syne',sans-serif;
  --font-body:'DM Sans',sans-serif;
  --font-mono:'DM Mono',monospace;
}
html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--font-body);-webkit-font-smoothing:antialiased;overflow:hidden;color-scheme:light}
button{font-family:inherit;cursor:pointer}
a{text-decoration:none;color:inherit}
input,select{font-family:inherit}

/* ── Topbar ─────────────────────────────────────────────────── */
.topbar{
  position:fixed;top:0;left:0;right:0;height:var(--topbar);
  background:var(--surface);border-bottom:1px solid var(--border);
  display:flex;align-items:center;padding:0 max(16px,env(safe-area-inset-left));gap:12px;z-index:200;box-shadow:0 1px 3px rgba(0,0,0,.04);
}
.topbar-logo{display:flex;align-items:center;gap:8px;font-family:var(--font-display);font-weight:800;font-size:14px;color:var(--text);flex-shrink:0;text-decoration:none}
.topbar-logo-icon{width:26px;height:26px;background:linear-gradient(135deg,var(--accent),var(--accent-d));border-radius:7px;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:0 3px 10px var(--accent-glow)}
.topbar-logo-icon svg{width:13px;height:13px;stroke:#000;fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.topbar-divider{width:1px;height:24px;background:var(--border2);margin:0 2px;flex-shrink:0}
.topbar-title{font-size:14px;font-weight:700;color:var(--text);flex:1}
.topbar-back{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:var(--muted2);padding:6px 12px;border-radius:8px;border:1px solid var(--border);background:none;transition:all .15s;flex-shrink:0}
.topbar-back:hover{color:var(--accent);border-color:var(--accent);background:var(--accent-pale)}
.topbar-back svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* ── Btn system ─────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:8px;font-size:13px;font-weight:600;border:none;cursor:pointer;transition:all .15s;white-space:nowrap;text-decoration:none}
.btn svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.btn-primary{background:linear-gradient(135deg,var(--accent),var(--accent-d));color:#fff}
.btn-primary:hover{opacity:.88;transform:translateY(-1px)}
.btn-ghost{background:transparent;color:var(--muted2);border:1px solid var(--border2)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-pale)}
.btn-danger{background:var(--red-pale);color:var(--red);border:1px solid rgba(239,68,68,0.2)}
.btn-danger:hover{background:var(--red);color:#fff}
.btn-sm{padding:6px 10px;font-size:12px;border-radius:7px}
.btn-icon{padding:7px;aspect-ratio:1}

/* ── Layout shell ───────────────────────────────────────────── */
.shell{
  display:flex;
  padding-top:var(--topbar);
  height:100vh;
  overflow:hidden;
  transition:padding-right .3s ease;
}
.shell.panel-open{padding-right:var(--panel)}

/* ── Sidebar ────────────────────────────────────────────────── */
.sidebar{
  width:var(--sidebar);flex-shrink:0;
  background:var(--surface);border-right:1px solid var(--border);
  display:flex;flex-direction:column;height:100%;overflow-y:auto;box-shadow:1px 0 0 var(--border);
}
.sidebar-section{padding:16px 12px 8px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--muted)}
.sidebar-form-btn{
  display:flex;align-items:center;gap:10px;
  width:100%;padding:10px 12px;border-radius:8px;
  border:none;background:none;color:var(--muted2);
  font-size:13px;font-weight:500;text-align:left;
  cursor:pointer;transition:all .15s;position:relative;
}
.sidebar-form-btn:hover{background:var(--card);color:var(--text)}
.sidebar-form-btn.active{background:var(--accent-pale);color:var(--accent);font-weight:600}
.sidebar-form-btn .sfb-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);flex-shrink:0;opacity:0;transition:opacity .15s}
.sidebar-form-btn.active .sfb-dot{opacity:1}
.sidebar-form-btn .sfb-count{margin-left:auto;font-size:11px;font-weight:700;color:var(--muted);background:var(--card2);padding:2px 7px;border-radius:20px;flex-shrink:0}
.sidebar-form-btn.active .sfb-count{background:var(--accent-pale);color:var(--accent)}
.sidebar-all{font-weight:700;color:var(--text)}

/* ── Main content ───────────────────────────────────────────── */
.main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}

/* ── Toolbar ────────────────────────────────────────────────── */
.toolbar{
  padding:14px 20px;background:var(--surface);border-bottom:1px solid var(--border);
  display:flex;align-items:center;gap:10px;flex-shrink:0;flex-wrap:wrap;
}
.search-wrap{position:relative;flex:1;min-width:180px;max-width:320px}
.search-wrap svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);width:15px;height:15px;stroke:var(--muted);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}
.search-input{width:100%;background:var(--card);border:1px solid var(--border2);border-radius:8px;padding:8px 12px 8px 34px;font-size:13px;color:var(--text);outline:none;transition:border .15s}
.search-input::placeholder{color:var(--muted)}
.search-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-pale)}
.select-filter{background:var(--card);border:1px solid var(--border2);border-radius:8px;padding:8px 12px;font-size:13px;color:var(--text);outline:none;cursor:pointer;transition:border .15s}
.select-filter:focus{border-color:var(--accent)}
.select-filter option{background:var(--card)}
.toolbar-right{margin-left:auto;display:flex;gap:8px;align-items:center;flex-shrink:0}
.result-count{font-size:12px;color:var(--muted);white-space:nowrap}

/* ── Table ──────────────────────────────────────────────────── */
.table-wrap{flex:1;overflow-y:auto;padding:0}
table{width:100%;border-collapse:collapse}
thead{position:sticky;top:0;z-index:10}
thead tr{background:var(--surface);border-bottom:1px solid var(--border)}
th{padding:10px 16px;text-align:left;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);white-space:nowrap;user-select:none}
th.sortable{cursor:pointer}
th.sortable:hover{color:var(--text)}
th .sort-arrow{display:inline-block;margin-left:4px;opacity:.4;transition:opacity .15s}
th.sort-asc .sort-arrow::after{content:'↑';opacity:1}
th.sort-desc .sort-arrow::after{content:'↓';opacity:1}
td{padding:12px 16px;border-bottom:1px solid var(--border);vertical-align:middle;font-size:13px}
tr.row-item{cursor:pointer;transition:background .1s}
tr.row-item:hover td{background:#fafbfc}
tr.row-item.selected td{background:var(--accent-pale);border-color:rgba(224,149,0,0.2)}
tr.row-item:last-child td{border-bottom:none}
td .ref-code{font-family:var(--font-mono);font-size:12px;color:var(--accent);background:var(--accent-pale);border:1px solid rgba(224,149,0,0.25);padding:2px 8px;border-radius:6px;white-space:nowrap}
td .form-name{font-weight:600;color:var(--text)}
td .meta{font-size:11px;color:var(--muted);margin-top:2px}
td .row-actions{display:flex;gap:6px;opacity:0;transition:opacity .15s}
tr.row-item:hover .row-actions{opacity:1}

/* ── Badge ──────────────────────────────────────────────────── */
.badge{display:inline-flex;align-items:center;gap:4px;padding:3px 9px;border-radius:20px;font-family:var(--font-mono);font-size:10px;font-weight:500;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap}
.badge::before{content:'';width:5px;height:5px;border-radius:50%;background:currentColor;flex-shrink:0}
.badge-green{background:var(--green-pale);color:var(--green)}
.badge-yellow{background:var(--yellow-pale);color:var(--yellow)}
.badge-red{background:var(--red-pale);color:var(--red)}
.badge-blue{background:var(--accent-pale);color:var(--accent);border:1px solid rgba(224,149,0,0.25)}
.badge-gray{background:var(--bg);color:var(--muted);border:1px solid var(--border)}

/* ── Empty & loading ────────────────────────────────────────── */
.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:80px 20px;color:var(--muted);text-align:center;flex:1}
.empty-state svg{width:52px;height:52px;stroke:var(--border);fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;margin-bottom:16px}
.empty-state h3{font-family:var(--font-display);font-size:15px;font-weight:700;color:var(--muted2);margin-bottom:6px}
.empty-state p{font-size:13px;line-height:1.6}
.spinner-row td{padding:40px;text-align:center}
.spinner{display:inline-block;width:20px;height:20px;border:2px solid #e8eaed;border-top-color:var(--accent);border-radius:50%;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* ── Pagination ─────────────────────────────────────────────── */
.pagination{display:flex;align-items:center;gap:6px;padding:14px 20px;padding-bottom:max(14px,env(safe-area-inset-bottom));background:var(--surface);border-top:1px solid var(--border);flex-shrink:0;justify-content:center}
.page-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:7px;font-size:13px;font-weight:600;border:1px solid var(--border2);background:none;color:var(--muted2);cursor:pointer;transition:all .15s}
.page-btn:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-pale)}
.page-btn.active{background:var(--accent);border-color:var(--accent);color:#fff}
.page-btn:disabled{opacity:.3;cursor:not-allowed}
.page-info{font-size:12px;color:var(--muted);padding:0 8px}

/* ── Slide-in panel ─────────────────────────────────────────── */
.detail-panel{
  position:fixed;top:var(--topbar);right:0;bottom:0;
  width:var(--panel);
  background:var(--surface);border-left:1px solid var(--border);box-shadow:-4px 0 16px rgba(0,0,0,.06);
  display:flex;flex-direction:column;
  transform:translateX(100%);
  transition:transform .3s cubic-bezier(.22,1,.36,1);
  z-index:100;overflow:hidden;
}
.detail-panel.open{transform:translateX(0)}
.panel-topbar{
  padding:16px 20px;border-bottom:1px solid var(--border);
  display:flex;align-items:center;gap:12px;flex-shrink:0;
}
.panel-topbar h2{font-size:14px;font-weight:700;flex:1;color:var(--text)}
.panel-close{width:28px;height:28px;border-radius:7px;border:1px solid var(--border2);background:none;display:flex;align-items:center;justify-content:center;color:var(--muted2);cursor:pointer;transition:all .15s;font-size:16px}
.panel-close:hover{background:var(--card);color:var(--text)}
.panel-body{flex:1;overflow-y:auto;padding:20px}
.panel-footer{padding:14px 20px;padding-bottom:max(14px,env(safe-area-inset-bottom));border-top:1px solid var(--border);display:flex;gap:8px;flex-shrink:0;flex-wrap:wrap}

/* ── Panel detail styles ────────────────────────────────────── */
.sub-meta-bar{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
.detail-section{margin-bottom:24px}
.detail-section-title{font-family:var(--font-mono);font-size:10px;font-weight:500;text-transform:uppercase;letter-spacing:.09em;color:var(--muted);margin-bottom:12px;display:flex;align-items:center;gap:6px}
.detail-section-title::after{content:'';flex:1;height:1px;background:var(--border)}
.field-row{display:flex;flex-direction:column;gap:4px;padding:10px 0;border-bottom:1px solid var(--border)}
.field-row:last-child{border-bottom:none}
.field-label{font-family:var(--font-mono);font-size:10px;font-weight:500;color:var(--muted2);text-transform:uppercase;letter-spacing:.08em}
.field-value{font-size:13px;color:var(--text);line-height:1.5;word-break:break-word}
.field-value a{color:var(--accent)}
.field-value a:hover{text-decoration:underline}
.sig-img{border:1px solid var(--border2);border-radius:8px;overflow:hidden;background:#fff;max-width:100%}
.sig-img img{display:block;width:100%;height:auto;max-height:120px;object-fit:contain}
.file-chip{display:inline-flex;align-items:center;gap:6px;padding:5px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;font-size:12px;color:var(--text);margin:2px;text-decoration:none}
.file-chip:hover{border-color:var(--accent);color:var(--accent)}
.file-chip svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.panel-loading{display:flex;align-items:center;justify-content:center;flex:1;flex-direction:column;gap:12px;color:var(--muted);font-size:13px}

/* ── Toast ──────────────────────────────────────────────────── */
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);background:#111827;color:#fff;padding:10px 20px;border-radius:999px;font-size:13px;font-weight:600;z-index:999;opacity:0;transition:all .3s;pointer-events:none;white-space:nowrap;box-shadow:0 4px 20px rgba(0,0,0,.18)}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.toast.ok{background:#166534;color:#fff}
.toast.err{background:#991b1b;color:#fff}


/* ── Bulk actions ────────────────────────────────────────────── */
.bulk-bar{
  display:none;align-items:center;gap:10px;
  padding:10px 20px;background:var(--accent-pale);
  border-bottom:1px solid var(--border-accent);flex-shrink:0;
}
.bulk-bar.visible{display:flex}
.bulk-count{font-family:var(--font-mono);font-size:12px;font-weight:500;color:var(--accent);flex:1}
.cb-wrap{display:flex;align-items:center;justify-content:center;width:36px}
.cb-wrap input[type=checkbox]{width:16px;height:16px;accent-color:var(--accent);cursor:pointer}
th .cb-wrap{display:flex;justify-content:center}
/* ── Responsive ─────────────────────────────────────────────── */
@media(max-width:900px){
  .sidebar{display:none}
  :root{--panel:100vw}
  .shell.panel-open{padding-right:0}
  /* Full-width slide-in panel on mobile */
  .detail-panel{width:100vw}
}
@media(max-width:600px){
  th.col-ip,td.col-ip{display:none}
  th.col-email,td.col-email{display:none}
  .toolbar{padding:8px 12px;gap:8px;flex-wrap:wrap}
  .toolbar-right{flex-wrap:wrap;gap:6px}
  .result-count{display:none}
  .search-wrap{min-width:140px;max-width:none;flex:1}
  /* Make table horizontally scrollable */
  .table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
  th,td{padding:10px 10px;font-size:12px}
  /* Panel topbar on mobile */
  .panel-topbar{padding:12px 16px}
  .panel-body{padding:16px}
  .panel-footer{padding:10px 16px;flex-wrap:wrap}
  /* Bulk bar */
  .bulk-bar{padding:8px 12px;flex-wrap:wrap;gap:8px}
  /* Topbar compress */
  .topbar{padding:0 12px;gap:8px}
  .topbar-logo{font-size:13px}
  .topbar-title{font-size:13px}
  .topbar-back{padding:5px 10px;font-size:12px}
  /* Pagination */
  .pagination{padding:10px 12px;gap:4px}
  .page-btn{width:28px;height:28px;font-size:12px}
}
@media(max-width:420px){
  .topbar-back span{display:none}
  .topbar-back::before{content:'←'}
  th,td{padding:8px 8px;font-size:11px}
  .ref-code{font-size:10px;padding:2px 6px}
}
</style>
</head>
<body>

<!-- ── Topbar ──────────────────────────────────────────────────── -->
<div class="topbar">
  <a class="topbar-logo" href="../admin.php">
    <div class="topbar-logo-icon"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg></div>
    FormBuilder
  </a>
  <div class="topbar-divider"></div>
  <span class="topbar-title">Submissions</span>
  <a class="topbar-back" href="../admin.php">
    <svg viewBox="0 0 24 24"><path d="M19 12H5M12 5l-7 7 7 7"/></svg>
    Dashboard
  </a>
</div>

<!-- ── Shell ───────────────────────────────────────────────────── -->
<div class="shell" id="shell">

  <!-- Sidebar: form list -->
  <aside class="sidebar" id="sidebar">
    <div class="sidebar-section">Forms</div>
    <div id="sidebar-forms">
      <button class="sidebar-form-btn sidebar-all active" data-fid="" onclick="selectForm('',this)">
        <span class="sfb-dot"></span>
        All Forms
        <span class="sfb-count" id="sfb-all">—</span>
      </button>
    </div>
  </aside>

  <!-- Main: toolbar + table -->
  <div class="main" id="main">
    <!-- Toolbar -->
    <div class="toolbar">
      <div class="search-wrap">
        <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
        <input class="search-input" id="search-input" type="text" placeholder="Search ref, form, data…" oninput="debounceSearch()"/>
      </div>
      <select class="select-filter" id="email-filter" onchange="applyFilters()">
        <option value="">All status</option>
        <option value="1">Email sent</option>
        <option value="0">Email pending</option>
      </select>
      <span class="result-count" id="result-count"></span>
      <div class="toolbar-right">
        <button class="btn btn-ghost btn-sm" id="export-btn" onclick="exportCsv()" title="Export CSV">
          <svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
          Export CSV
        </button>
      </div>
    </div>

    <!-- Bulk actions bar -->
    <div class="bulk-bar" id="bulk-bar">
      <span class="bulk-count" id="bulk-count">0 selected</span>
      <button class="btn btn-danger btn-sm" onclick="bulkDelete()">
        <svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
        Delete Selected
      </button>
      <button class="btn btn-ghost btn-sm" onclick="clearSelection()">Clear</button>
    </div>

    <!-- Table -->
    <div class="table-wrap" id="table-wrap">
      <table>
        <thead>
          <tr>
            <th style="width:40px"><div class="cb-wrap"><input type="checkbox" id="select-all" onchange="toggleSelectAll(this)" title="Select all"/></div></th>
            <th>Ref</th>
            <th>Form</th>
            <th class="col-email">Email</th>
            <th>PDF</th>
            <th class="col-ip">IP</th>
            <th class="sortable" onclick="toggleSort()" id="sort-th">
              Submitted <span class="sort-arrow"></span>
            </th>
            <th style="width:90px"></th>
          </tr>
        </thead>
        <tbody id="tbody">
          <tr class="spinner-row"><td colspan="8"><span class="spinner"></span></td></tr>
        </tbody>
      </table>
    </div>
    <div class="pagination" id="pagination"></div>
  </div>
</div>

<!-- ── Detail panel ────────────────────────────────────────────── -->
<div class="detail-panel" id="detail-panel">
  <div class="panel-topbar">
    <h2 id="panel-title">Submission</h2>
    <button class="panel-close" onclick="closePanel()">✕</button>
  </div>
  <div class="panel-body" id="panel-body">
    <div class="panel-loading"><span class="spinner"></span> Loading…</div>
  </div>
  <div class="panel-footer" id="panel-footer"></div>
</div>

<div class="toast" id="toast"></div>

<script>
// ── Config ────────────────────────────────────────────────────
const BASE = (()=>{
  const p = window.location.pathname;
  const d = p.endsWith('/') ? p.slice(0,-1) : p.substring(0, p.lastIndexOf('/'));
  return d.replace(/\/admin(\/submissions)?(.php)?$/, '');
})();
const API = BASE + '/api';

// ── State ─────────────────────────────────────────────────────
let state = {
  forms: [],
  formId: '',
  search: '',
  emailFilter: '',
  page: 1,
  limit: 30,
  total: 0,
  sortDir: 'desc',
  selectedId: null,
  panelOpen: false,
};
let searchTimer = null;

// ── Utils ─────────────────────────────────────────────────────
function $id(id) { return document.getElementById(id); }
function esc(s) {
  return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function fmt(dt) {
  if (!dt) return '—';
  const d = new Date(dt);
  return d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'});
}
function fmtBytes(b) {
  if (b < 1024) return b + ' B';
  if (b < 1024*1024) return (b/1024).toFixed(1) + ' KB';
  return (b/1024/1024).toFixed(2) + ' MB';
}
function toast(msg, type='') {
  const t = $id('toast');
  t.textContent = msg;
  t.className = 'toast show ' + type;
  clearTimeout(t._t);
  t._t = setTimeout(() => t.className = 'toast', 3200);
}

async function api(path, method='GET', body) {
  const opts = {method, credentials:'same-origin', headers:{}};
  if (body) { opts.headers['Content-Type'] = 'application/json'; opts.body = JSON.stringify(body); }
  const r = await fetch(API + path, opts);
  if (!r.ok && r.status === 401) { location.href = '../admin.php'; return; }
  return r.json();
}

// ── Boot ──────────────────────────────────────────────────────
async function boot() {
  // Load forms for sidebar + counts
  const j = await api('/forms');
  state.forms = j.forms || [];

  // Build sidebar
  const sb = $id('sidebar-forms');
  // total count
  const statsJ = await api('/stats');
  if (statsJ.ok) {
    $id('sfb-all').textContent = statsJ.total_submissions;
  }
  state.forms.forEach(f => {
    const btn = document.createElement('button');
    btn.className = 'sidebar-form-btn';
    btn.dataset.fid = f.id;
    btn.onclick = () => selectForm(String(f.id), btn);
    btn.innerHTML = `
      <span class="sfb-dot"></span>
      <span style="flex:1;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">${esc(f.title)}</span>
      <span class="sfb-count">${f.sub_count ?? '—'}</span>`;
    sb.appendChild(btn);
  });

  // Check URL param
  const params = new URLSearchParams(location.search);
  if (params.get('form_id')) {
    const fid = params.get('form_id');
    state.formId = fid;
    const btn = sb.querySelector(`[data-fid="${fid}"]`);
    if (btn) {
      sb.querySelector('.active')?.classList.remove('active');
      btn.classList.add('active');
    }
  }

  loadSubmissions();
}

function selectForm(fid, btn) {
  state.formId = fid;
  state.page = 1;
  document.querySelectorAll('.sidebar-form-btn').forEach(b => b.classList.remove('active'));
  btn.classList.add('active');
  loadSubmissions();
}

// ── Load submissions ──────────────────────────────────────────
async function loadSubmissions() {
  clearSelection();
  $id('tbody').innerHTML = '<tr class="spinner-row"><td colspan="8"><span class="spinner"></span></td></tr>';
  $id('pagination').innerHTML = '';
  $id('result-count').textContent = '';

  let url;
  const params = new URLSearchParams();
  params.set('page', state.page);
  params.set('limit', state.limit);
  if (state.search) params.set('search', state.search);
  if (state.emailFilter !== '') params.set('email_sent', state.emailFilter);
  params.set('sort', state.sortDir);

  if (state.formId) {
    url = `/forms/${state.formId}/submissions?${params}`;
  } else {
    url = `/submissions/all?${params}`;
  }

  const j = await api(url);

  if (!j || !j.ok) {
    // Fallback: if /submissions/all doesn't exist, use stats recent
    await loadFallback();
    return;
  }

  state.total = j.total || 0;
  renderTable(j.rows || []);
  renderPagination();
}

async function loadFallback() {
  // For 'all forms' when advanced endpoint not available
  if (!state.formId) {
    const statsJ = await api('/stats');
    const rows = statsJ.recent || [];
    // Enrich rows with fake fields for rendering
    const enriched = rows.map(r => ({
      id: r.id || 0,
      ref: r.ref,
      form_title: r.title,
      email_sent: r.email_sent ?? null,
      pdf_path: r.pdf_path ?? null,
      ip: r.ip ?? '',
      created_at: r.created_at
    }));
    state.total = enriched.length;
    renderTable(enriched);
    $id('result-count').textContent = 'Showing recent 5';
    return;
  }
  // Specific form
  const params = new URLSearchParams();
  params.set('page', state.page);
  params.set('limit', state.limit);
  const j = await api(`/forms/${state.formId}/submissions?${params}`);
  state.total = j.total || 0;
  const form = state.forms.find(f => String(f.id) === String(state.formId));
  (j.rows || []).forEach(r => { r.form_title = form?.title || ''; });
  renderTable(j.rows || []);
  renderPagination();
}

function renderTable(rows) {
  const sortThEl = $id('sort-th');
  sortThEl.className = 'sortable sort-' + state.sortDir;

  $id('result-count').textContent = `${state.total.toLocaleString()} submission${state.total !== 1 ? 's' : ''}`;

  if (!rows.length) {
    $id('tbody').innerHTML = `
      <tr><td colspan="8" style="padding:0">
        <div class="empty-state" style="padding:60px 20px">
          <svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
          <h3>No submissions found</h3>
          <p>${state.search ? 'Try a different search.' : 'Submissions will appear here when forms are filled out.'}</p>
        </div>
      </td></tr>`;
    return;
  }

  $id('tbody').innerHTML = rows.map(r => {
    const selected = r.id && r.id === state.selectedId ? 'selected' : '';
    const emailBadge = r.email_sent === null
      ? `<span class="badge badge-gray">—</span>`
      : r.email_sent
        ? `<span class="badge badge-green">Sent</span>`
        : `<span class="badge badge-yellow">Pending</span>`;
    const pdfBtn = r.pdf_path
      ? `<a href="${API}/submissions/${r.id}/pdf" class="btn btn-ghost btn-sm btn-icon" target="_blank" title="Download PDF" onclick="event.stopPropagation()"><svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></a>`
      : `<span style="color:var(--muted);font-size:12px">—</span>`;

    return `
      <tr class="row-item ${selected}" data-id="${r.id}">
        <td onclick="event.stopPropagation()"><div class="cb-wrap"><input type="checkbox" class="row-cb" data-id="${r.id}" onchange="onRowCheck(this)"/></div></td>
        <td onclick="openPanel(${r.id})"><span class="ref-code">${esc(r.ref)}</span></td>
        <td onclick="openPanel(${r.id})">
          <div class="form-name">${esc(r.form_title || '—')}</div>
        </td>
        <td class="col-email" onclick="openPanel(${r.id})">${emailBadge}</td>
        <td onclick="openPanel(${r.id})">${pdfBtn}</td>
        <td class="col-ip" onclick="openPanel(${r.id})" style="font-size:11px;color:var(--muted)">${esc(r.ip || '—')}</td>
        <td style="color:var(--muted2);font-size:12px;white-space:nowrap">${fmt(r.created_at)}</td>
        <td>
          <div class="row-actions">
            <button class="btn btn-ghost btn-sm btn-icon" onclick="event.stopPropagation();openPanel(${r.id})" title="View">
              <svg viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
            </button>
            <button class="btn btn-danger btn-sm btn-icon" onclick="event.stopPropagation();deleteSub(${r.id},'${esc(r.ref)}')" title="Delete">
              <svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
            </button>
          </div>
        </td>
      </tr>`;
  }).join('');
}

function renderPagination() {
  const pages = Math.ceil(state.total / state.limit);
  if (pages <= 1) { $id('pagination').innerHTML = ''; return; }

  const p = state.page;
  let html = `<button class="page-btn" onclick="goPage(${p-1})" ${p<=1?'disabled':''}>‹</button>`;

  let start = Math.max(1, p-2), end = Math.min(pages, p+2);
  if (start > 1) { html += `<button class="page-btn" onclick="goPage(1)">1</button>`; if(start>2) html += `<span class="page-info">…</span>`; }
  for (let i = start; i <= end; i++) {
    html += `<button class="page-btn ${i===p?'active':''}" onclick="goPage(${i})">${i}</button>`;
  }
  if (end < pages) { if(end<pages-1) html += `<span class="page-info">…</span>`; html += `<button class="page-btn" onclick="goPage(${pages})">${pages}</button>`; }
  html += `<button class="page-btn" onclick="goPage(${p+1})" ${p>=pages?'disabled':''}>›</button>`;
  html += `<span class="page-info">${((p-1)*state.limit+1)}–${Math.min(p*state.limit,state.total)} of ${state.total}</span>`;

  $id('pagination').innerHTML = html;
}

function goPage(p) {
  state.page = p;
  loadSubmissions();
  $id('table-wrap').scrollTop = 0;
}

function toggleSort() {
  state.sortDir = state.sortDir === 'desc' ? 'asc' : 'desc';
  state.page = 1;
  loadSubmissions();
}

// ── Search & filter ───────────────────────────────────────────
function debounceSearch() {
  clearTimeout(searchTimer);
  searchTimer = setTimeout(() => {
    state.search = $id('search-input').value.trim();
    state.page = 1;
    loadSubmissions();
  }, 320);
}

function applyFilters() {
  state.emailFilter = $id('email-filter').value;
  state.page = 1;
  loadSubmissions();
}

// ── Detail panel ──────────────────────────────────────────────
async function openPanel(id) {
  if (!id) return;
  state.selectedId = id;
  state.panelOpen = true;

  // Highlight selected row
  document.querySelectorAll('.row-item').forEach(r => {
    r.classList.toggle('selected', parseInt(r.dataset.id) === id);
  });

  $id('shell').classList.add('panel-open');
  $id('detail-panel').classList.add('open');
  $id('panel-title').textContent = 'Loading…';
  $id('panel-body').innerHTML = '<div class="panel-loading"><span class="spinner"></span> Loading submission…</div>';
  $id('panel-footer').innerHTML = '';

  const j = await api('/submissions/' + id);
  if (!j || !j.ok) {
    $id('panel-body').innerHTML = '<p style="color:var(--red);padding:20px">Failed to load submission.</p>';
    return;
  }

  const s = j.submission;
  const fields = s.fields || [];
  const data   = s.data   || {};
  const files  = s.files  || {};

  $id('panel-title').textContent = s.ref;

  // Meta badges
  const emailBadge = s.email_sent
    ? `<span class="badge badge-green">Email sent</span>`
    : `<span class="badge badge-yellow">Email pending</span>`;

  let fieldsHtml = '';
  if (fields.length) {
    fields.forEach(f => {
      const fid  = f.id || '';
      const type = f.type || 'text';
      let valHtml = '<span style="color:var(--muted)">—</span>';

      if (type === 'signature') {
        const sig = data[fid];
        if (sig && sig.startsWith('data:image')) {
          valHtml = `<div class="sig-img"><img src="${esc(sig)}" alt="Signature"/></div>`;
        }
      } else if (type === 'file') {
        const fl = files[fid] || [];
        if (fl.length) {
          valHtml = fl.map(fi =>
            `<a class="file-chip" href="${esc(fi.url||'')}" target="_blank">
              <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
              ${esc(fi.original_name||'file')}
              ${fi.size ? `<span style="color:var(--muted);margin-left:2px">${fmtBytes(fi.size)}</span>` : ''}
            </a>`
          ).join('');
        }
      } else {
        let v = data[fid] ?? '';
        if (Array.isArray(v)) v = v.filter(Boolean).join(', ');
        if (v) valHtml = `<span>${esc(String(v))}</span>`;
      }

      fieldsHtml += `
        <div class="field-row">
          <div class="field-label">${esc(f.label || fid)}</div>
          <div class="field-value">${valHtml}</div>
        </div>`;
    });
  } else {
    // No field definitions: show raw JSON
    Object.entries(data).forEach(([k, v]) => {
      let display = Array.isArray(v) ? v.join(', ') : String(v ?? '');
      if (display.startsWith('data:image')) display = '[signature image]';
      fieldsHtml += `
        <div class="field-row">
          <div class="field-label">${esc(k)}</div>
          <div class="field-value">${esc(display) || '<span style="color:var(--muted)">—</span>'}</div>
        </div>`;
    });
  }

  $id('panel-body').innerHTML = `
    <div class="sub-meta-bar">
      <span class="badge badge-blue">${esc(s.ref)}</span>
      ${emailBadge}
      <span class="badge badge-gray">${fmt(s.created_at)}</span>
      ${s.form_title ? `<span class="badge badge-gray">${esc(s.form_title)}</span>` : ''}
    </div>
    <div class="detail-section">
      <div class="detail-section-title">Form Responses</div>
      ${fieldsHtml || '<p style="color:var(--muted);font-size:13px">No field data.</p>'}
    </div>
    <div class="detail-section">
      <div class="detail-section-title">Metadata</div>
      <div class="field-row">
        <div class="field-label">IP Address</div>
        <div class="field-value">${esc(s.ip || '—')}</div>
      </div>
      <div class="field-row">
        <div class="field-label">User Agent</div>
        <div class="field-value" style="font-size:11px;color:var(--muted2)">${esc(s.user_agent || '—')}</div>
      </div>
    </div>`;

  $id('panel-footer').innerHTML = `
    ${s.pdf_path
      ? `<a href="${API}/submissions/${id}/pdf" class="btn btn-ghost" target="_blank">
           <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
           Download PDF
         </a>`
      : ''}
    <button class="btn btn-danger" onclick="deleteSub(${id},'${esc(s.ref)}')">
      <svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
      Delete
    </button>
    <button class="btn btn-ghost" onclick="closePanel()" style="margin-left:auto">Close</button>`;
}

function closePanel() {
  state.panelOpen = false;
  state.selectedId = null;
  $id('shell').classList.remove('panel-open');
  $id('detail-panel').classList.remove('open');
  document.querySelectorAll('.row-item').forEach(r => r.classList.remove('selected'));
}

document.addEventListener('keydown', e => { if (e.key === 'Escape') closePanel(); });

// ── Delete ────────────────────────────────────────────────────
async function deleteSub(id, ref) {
  if (!confirm(`Delete submission ${ref}?\nThis cannot be undone.`)) return;
  const j = await api('/submissions/' + id, 'DELETE');
  if (j.ok) {
    toast('Submission deleted', 'ok');
    if (state.selectedId === id) closePanel();
    await loadSubmissions();
  } else {
    toast(j.error || 'Delete failed', 'err');
  }
}

// ── Export CSV ────────────────────────────────────────────────
function exportCsv() {
  if (!state.formId) {
    toast('Select a specific form to export CSV', 'err');
    return;
  }
  // CsvExporter is server-side; just redirect
  window.open(`${BASE}/api/forms/${state.formId}/submissions/csv`, '_blank');
}


// ── Bulk selection ────────────────────────────────────────────
let selectedIds = new Set();

function onRowCheck(cb) {
  const id = parseInt(cb.dataset.id);
  if (cb.checked) selectedIds.add(id);
  else selectedIds.delete(id);
  updateBulkBar();
}

function toggleSelectAll(cb) {
  document.querySelectorAll('.row-cb').forEach(el => {
    el.checked = cb.checked;
    const id = parseInt(el.dataset.id);
    if (cb.checked) selectedIds.add(id);
    else selectedIds.delete(id);
  });
  updateBulkBar();
}

function clearSelection() {
  selectedIds.clear();
  document.querySelectorAll('.row-cb').forEach(el => el.checked = false);
  const sa = $id('select-all'); if (sa) sa.checked = false;
  updateBulkBar();
}

function updateBulkBar() {
  const bar = $id('bulk-bar');
  const n = selectedIds.size;
  if (n > 0) {
    bar.classList.add('visible');
    $id('bulk-count').textContent = n + ' selected';
  } else {
    bar.classList.remove('visible');
  }
}

async function bulkDelete() {
  const n = selectedIds.size;
  if (!n) return;
  if (!confirm(`Delete ${n} submission${n !== 1 ? 's' : ''}? This cannot be undone.`)) return;
  const ids = [...selectedIds];
  let failed = 0;
  for (const id of ids) {
    const j = await api('/submissions/' + id, 'DELETE');
    if (!j.ok) failed++;
    else { selectedIds.delete(id); if (state.selectedId === id) closePanel(); }
  }
  clearSelection();
  if (failed) toast(`Deleted ${n - failed}, ${failed} failed`, 'warn');
  else toast(`Deleted ${n} submission${n !== 1 ? 's' : ''}`, 'ok');
  await loadSubmissions();
}

// Clear selection when loading new data
const _origLoadSubs = loadSubmissions;

// ── Boot ──────────────────────────────────────────────────────
boot();
</script>
</body>
</html>
