/* public/js/dashboard.js — works for both customer and superadmin */
(async function () {
  function csrf() { const m = document.cookie.match(/(?:^|; )ncsrf=([^;]+)/); return m ? m[1] : ''; }
  async function api(p, o = {}) {
    const r = await fetch(p, {
      ...o,
      headers: { 'content-type': 'application/json', 'x-csrf-token': csrf(), ...(o.headers || {}) },
    });
    if (r.status === 401) { location.href = '/login?next=' + encodeURIComponent(location.pathname); return null; }
    try { return await r.json(); } catch { return {}; }
  }
  function esc(s) { return String(s || '').replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])); }
  function pct(u, l) { return l ? Math.min(100, Math.round((u / l) * 100)) : 0; }
  function setBar(sel, p) {
    const el = document.querySelector(sel);
    if (!el) return;
    el.style.width = p + '%';
    el.parentElement.classList.toggle('warn',   p >= 70 && p < 90);
    el.parentElement.classList.toggle('danger', p >= 90);
  }
  function setText(id, v) { const e = document.getElementById(id); if (e) e.textContent = v; }
  function statusPill(s) {
    return `<span class="pill ${s==='open'?'warn':s==='closed'?'ok':'info'}">${s}</span>`;
  }
  function fmtDate(ms) {
    return ms ? new Date(ms).toLocaleDateString() : '—';
  }
  function fmtDateTime(ms) {
    return ms ? new Date(ms).toLocaleString() : '—';
  }
  function fmtHours(hours) {
    if (hours === null || hours === undefined || Number.isNaN(hours)) return '—';
    if (hours < 1) return Math.round(hours * 60) + 'm';
    if (hours < 48) return hours.toFixed(hours < 10 ? 1 : 0) + 'h';
    return (hours / 24).toFixed(1) + 'd';
  }

  // ---- load me ----
  const me = await api('/auth/me');
  if (!me) return;
  const u = me.user;
  setText('who', u.name.split(' ')[0]);
  setText('avatar', (u.name || u.email)[0].toUpperCase());
  setText('planPill', (u.plan_details && u.plan_details.name) || u.plan);

  // hide admin nav for non-admins (visible by default with .hidden)
  if (u.role === 'superadmin') {
    document.querySelectorAll('[data-admin-only]').forEach(a => a.classList.remove('hidden'));
  }

  // ---- load summary ----
  const s = await api('/dashboard/summary');
  if (!s) return;
  const plan = s.plan, usage = s.usage, limits = s.limits;
  const activeCases = Number.isFinite(Number(s.active_cases)) ? Number(s.active_cases) : (s.recent_cases || []).length;

  setText('planName', plan.name);
  setText('planPrice', plan.price_cents ? `$${(plan.price_cents/100).toFixed(2)}/mo` : 'Free');

  setText('casesUsed', activeCases);
  setText('scansUsed', usage.scans);
  setText('takedownsUsed', usage.takedowns);
  setText('casesLimit', limits.cases);
  setText('scansLimit', limits.scans);
  setText('takedownsLimit', limits.takedowns);
  setText('casesHint',     `${Math.max(0, limits.cases - activeCases)} active slot(s) left`);
  setText('scansHint',     `resets monthly`);
  setText('takedownsHint', `resets monthly`);

  setBar('#casesBar',     pct(activeCases, limits.cases));
  setBar('#scansBar',     pct(usage.scans, limits.scans));
  setBar('#takedownsBar', pct(usage.takedowns, limits.takedowns));

  // ---- takedown outcome snapshot (3 cards, down from 6) ----
  const metrics = s.takedown_metrics || {};
  const outcomeCards = document.getElementById('outcomeCards');
  if (outcomeCards) {
    const cards = [
      { cls: 'ok',     title: 'Removed',    value: metrics.removed || 0, sub: `${metrics.success_rate || 0}% resolved` },
      { cls: 'danger', title: 'Still live', value: metrics.still_live || 0, sub: 'latest probe' },
      { cls: 'warn',   title: 'In flight',   value: (metrics.in_flight || 0) + (metrics.pending || 0), sub: 'sent or queued' },
    ];
    outcomeCards.innerHTML = cards.map(c => `
      <div class="card ${c.cls}">
        <h3>${c.title}</h3>
        <div class="v">${c.value}</div>
        <div class="sub">${c.sub}</div>
      </div>
    `).join('');
  }

  const removedRows = document.getElementById('removedRows');
  if (removedRows) {
    const removed = metrics.recent_removed || [];
    setText('removedCountLabel', removed.length ? `${metrics.removed || removed.length} total` : '');
    removedRows.innerHTML = removed.length
      ? removed.map(t => {
          const hours = t.resolved_at && t.sent_at ? (t.resolved_at - t.sent_at) / 3600000 : null;
          return `<tr>
            <td class="muted mono">${fmtDate(t.resolved_at)}</td>
            <td>${esc(t.domain || '—')}</td>
            <td class="mono muted">${esc(t.contact_email || '—')}</td>
            <td>${fmtHours(hours)}</td>
          </tr>`;
        }).join('')
      : `<tr><td colspan="4" class="muted center">No confirmed removals yet.</td></tr>`;
  }

  const domainStats = document.getElementById('domainStats');
  if (domainStats) {
    const domains = metrics.by_domain || [];
    domainStats.innerHTML = domains.length
      ? domains.map(d => {
          const rate = d.total ? Math.round(((d.removed || 0) / d.total) * 100) : 0;
          return `<div class="domain-row">
            <div class="between">
              <strong>${esc(d.domain || 'unknown')}</strong>
              <span class="muted">${d.removed || 0}/${d.total || 0} removed</span>
            </div>
            <div class="bar"><span style="width:${rate}%"></span></div>
            <div class="domain-meta">
              <span>${d.in_flight || 0} in flight</span>
              <span>${d.failed || 0} failed</span>
            </div>
          </div>`;
        }).join('')
      : `<div class="muted center domain-empty">No takedown domains yet.</div>`;
  }

  // ---- recent cases ----
  const casesRows = document.getElementById('casesRows');
  if (casesRows) {
    casesRows.innerHTML = s.recent_cases.length
      ? s.recent_cases.map(c => `
        <tr>
          <td>${c.id}</td>
          <td>${esc(c.victim_name)}</td>
          <td>${statusPill(c.status)}</td>
          <td class="muted mono">—</td>
          <td class="muted mono">${new Date(c.created_at).toLocaleDateString()}</td>
          <td><a href="/scan/?case_id=${c.id}">scan</a> · <a href="/takedown/?case_id=${c.id}">takedown</a></td>
        </tr>`).join('')
      : `<tr><td colspan="6" class="muted center">No cases yet. <a href="/cases/">Create one</a>.</td></tr>`;
  }

  // ---- recent findings ----
  const fRows = document.getElementById('findingsRows');
  if (fRows) {
    const fmtUrl = (full) => {
      if (!full) return '—';
      try {
        const u = new URL(full);
        const path = u.pathname === '/' ? '/' : u.pathname;
        const display = u.host + path;
        if (display.length <= 70) return display;
        const head = display.slice(0, 35);
        const tail = display.slice(-30);
        return head + '…' + tail;
      } catch (_) { return full; }
    };
    fRows.innerHTML = s.recent_findings.length
      ? s.recent_findings.map(f => `
        <tr>
          <td class="muted mono">${new Date(f.created_at).toLocaleDateString()}</td>
          <td class="url-cell" title="${esc(f.url)}"><a href="${esc(f.url)}" target="_blank" rel="noopener">${esc(fmtUrl(f.url))}</a></td>
          <td><span class="pill info">T${f.tier}</span></td>
          <td class="muted">—</td>
          <td>${statusPill(f.status)}</td>
          <td><a href="/takedown/?case_id=${f.case_id}&finding_id=${f.id}&url=${encodeURIComponent(f.url)}">Takedown →</a></td>
        </tr>`).join('')
      : `<tr><td colspan="6" class="muted center">No leaks detected yet. <a href="/scan/">Run a scan</a>.</td></tr>`;
  }

  // ---- takedowns: KPI cards + full history with state machine ----
  const tdCountEl = document.getElementById('tdCount');
  const tRows = document.getElementById('takedownsRows');
  const fmtUrl = (full) => {
    if (!full) return '—';
    try {
      const u = new URL(full);
      const path = u.pathname === '/' ? '/' : u.pathname;
      const display = u.host + path;
      if (display.length <= 70) return display;
      const head = display.slice(0, 35);
      const tail = display.slice(-30);
      return head + '…' + tail;
    } catch (_) { return full; }
  };

  // State machine visual map
  const STATE = {
    production_pending:  { cls: 'warn',   label: 'pending' },
    sent:               { cls: 'info',   label: 'sent' },
    escalated:          { cls: 'warn',   label: 'sent' },      // historical — same as sent
    removed:            { cls: 'ok',     label: 'REMOVED' },
    responded:          { cls: 'ok',     label: 'responded' },
    failed:             { cls: 'danger', label: 'failed' },
    chain_only:         { cls: '',       label: 'chain only' },
  };
  function tdStatePill(s) {
    const m = STATE[s] || { cls: '', label: s || '?' };
    return `<span class="pill ${m.cls}">${m.label}</span>`;
  }
  // Health pill — 'live' / 'removed' / 'blocked' / 'soft_404' / 'dns_fail'
  const HEALTH = {
    live:      { cls: 'danger', label: 'still live' },
    removed:   { cls: 'ok',     label: 'REMOVED' },
    soft_404:  { cls: 'ok',     label: 'REMOVED (soft 404)' },
    blocked:   { cls: 'warn',   label: 'probe blocked' },
    dns_fail:  { cls: 'warn',   label: 'host unreachable' },
    error:     { cls: 'danger', label: 'probe error' },
  };
  function healthPill(h, http) {
    if (!h) return '<span class="muted">— never probed —</span>';
    const m = HEALTH[h] || { cls: '', label: h };
    return `<span class="pill ${m.cls}" title="HTTP ${http || '?'}">${m.label}</span>`;
  }

  // KPI cards
  const kpis = document.getElementById('tdKpis');
  if (kpis) {
    const bd = {}; for (const row of (s.takedown_breakdown || [])) bd[row.status] = row.n;
    const total = Object.values(bd).reduce((a,b) => a+b, 0);
    const cards = [
      { t: 'Total', v: total, sub: 'all takedowns' },
      { t: 'Sent', v: (bd.sent || 0) + (bd.escalated || 0), sub: 'in flight' },
      { t: 'Removed', v: bd.removed || 0, sub: 'confirmed by re-probe' },
      { t: 'Responded', v: bd.responded || 0, sub: 'operator-marked' },
      { t: 'Failed', v: bd.failed || 0, sub: 'Resend 4xx/5xx' },
      { t: 'Pending', v: bd.production_pending || 0, sub: 'not sent yet' },
    ];
    kpis.innerHTML = cards.map(c => `<div class="card"><h3>${c.t}</h3><div class="v">${c.v}</div><div class="sub">${c.sub}</div></div>`).join('');
  }
  if (tdCountEl) {
    const n = (s.takedown_history || []).length;
    tdCountEl.textContent = n ? '(' + n + ' total)' : '';
  }

  if (tRows) {
    const rows = s.takedown_history || [];
    if (!rows.length) {
      tRows.innerHTML = '<tr><td colspan="9" class="muted center">No takedowns yet. <a href="/takedown/">Send one</a>.</td></tr>';
    } else {
      // Fetch note counts in batch so we can render a badge per row.
      let noteCounts = {};
      if (u.role === 'superadmin' && rows.length) {
        try {
          const ids = rows.map(t => t.id).join(',');
          const r = await api('/takedown/notes-counts?ids=' + encodeURIComponent(ids));
          if (r && r.counts) noteCounts = r.counts;
        } catch (_) { /* non-fatal */ }
      }
      tRows.innerHTML = rows.map(t => {
        const created = t.created_at ? fmtDateTime(t.created_at) : '—';
        const sent = t.sent_at ? fmtDateTime(t.sent_at) : '<span class="muted">queued</span>';
        const escalated = t.escalated_at ? fmtDateTime(t.escalated_at) : '<span class="muted">—</span>';
        const removed = t.resolved_at ? fmtDateTime(t.resolved_at) : '<span class="muted">—</span>';
        const probeReason = t.last_health_reason ? `<div class="muted small-text">${esc(t.last_health_reason)}</div>` : '';
        const probe = t.last_health_check ? fmtDateTime(t.last_health_check) + ' · ' + healthPill(t.last_health_status, t.last_http_status) + probeReason : '<span class="muted">never</span>';
        // Compact action: one "⋯" menu button per row instead of 4 inline buttons.
        // All actions still work the same; the menu just hides them until clicked.
        const isSuper = u.role === 'superadmin';
        const isOwner = t.user_id && t.user_id === u.id;
        const nNotes = noteCounts[t.id] || 0;
        const actions = [];
        if (isSuper) actions.push({ act: 'check',   label: '🔄 Check now' });
        if (isSuper || isOwner) actions.push({ act: 'notes',  label: nNotes ? `📝 Notes (${nNotes})` : '📝 Add note' });
        actions.push({ act: 'removed',  label: '✓ Mark removed' });
        actions.push({ act: 'responded', label: '💬 Mark responded' });
        const menuHtml = actions.length
          ? `<div class="td-menu">
               <button class="td-menu-btn" data-id="${t.id}">Actions ▾</button>
               <div class="td-menu-popup hidden" data-id="${t.id}">
                 ${actions.map(a => `<button class="td-act" data-act="${a.act}" data-id="${t.id}">${esc(a.label)}</button>`).join('')}
               </div>
             </div>`
          : '';
        return `
          <tr>
            <td data-label="Select"><input type="checkbox" class="td-check" data-id="${t.id}" /></td>
            <td data-label="State">${tdStatePill(t.status)}</td>
            <td data-label="When" class="muted mono">${created}</td>
            <td data-label="URL" class="url-cell" title="${esc(t.url)}"><a href="${esc(t.url)}" target="_blank" rel="noopener">${esc(fmtUrl(t.url))}</a></td>
            <td data-label="Platform">${esc(t.domain || '—')}</td>
            <td data-label="Recipient" class="mono small-text">${esc(t.contact_email || '—')}</td>
            <td data-label="Sent" class="muted mono">${sent}</td>
            <td data-label="Escalated" class="muted mono">${escalated}</td>
            <td data-label="Removed" class="muted mono">${removed}</td>
            <td data-label="Last probe">${probe}</td>
            <td data-label="Action">${menuHtml}</td>
            </td>
          </tr>`;
      }).join('');
    }
  }

  // ---- superadmin extras ----
  if (u.role === 'superadmin') {
    const a = await api('/admin/analytics');
    if (a) {
      const block = document.getElementById('adminOnly');
      if (block) block.style.display = '';
      const k = [
        ['Users', a.users.total, ''],
        ['MRR (est.)', '$' + a.users.mrr_estimate.toFixed(0), 'by plan × price'],
        ['Takedowns / 30d', a.takedowns.last_30_days.reduce((s,d)=>s+d.n,0), 'sent'],
        ['AI cost / 30d', '$' + a.ai.cost_this_month_usd.toFixed(2), `${a.ai.calls_this_month} calls`],
        ['AI avoided', a.ai.ai_avoided_pct + '%', 'free tier coverage'],
        ['Failed payments', a.billing.failed_payments, 'flagged'],
      ];
      const kpis = document.getElementById('adminKpis');
      if (kpis) kpis.innerHTML = k.map(([t,v,s2]) => `<div class="card"><h3>${t}</h3><div class="v">${v}</div><div class="sub">${s2||''}</div></div>`).join('');
    }
  }

  // ---- bulk actions + Check now + CSV export ----
  // Wire the bulk-action buttons. Selected = checkboxes in the takedowns table.
  const selected = () => Array.from(document.querySelectorAll('input.td-check:checked')).map(i => parseInt(i.dataset.id, 10));
  const updateBulkCount = () => {
    const n = selected().length;
    const el = document.getElementById('bulkCount');
    if (el) el.textContent = n ? `(${n} selected)` : '';
    const tb = document.getElementById('bulkToolbar');
    if (tb) tb.style.display = n ? 'flex' : 'none';
  };
  // delegate click on check-all
  document.addEventListener('change', (e) => {
    if (e.target.id === 'tdCheckAll') {
      document.querySelectorAll('input.td-check').forEach(cb => { cb.checked = e.target.checked; updateBulkCount(); });
    } else if (e.target.classList && e.target.classList.contains('td-check')) {
      updateBulkCount();
    }
  });

  async function bulkAction(action) {
    const ids = selected();
    if (!ids.length) { alert('Select takedowns first (tick the checkboxes).'); return; }
    if (action === 'mark_removed' || action === 'mark_responded' || action === 'mark_failed' || action === 'requeue') {
      if (!confirm('Apply "' + action + '" to ' + ids.length + ' takedown(s)?')) return;
      const r = await api('/takedown/bulk', { method: 'POST', body: JSON.stringify({ ids, action }) });
      if (r && r.ok) {
        alert('Updated ' + r.updated + ' / ' + r.total + ' (' + r.skipped + ' skipped, ' + r.errors + ' errors)');
        location.reload();
      } else {
        alert('Bulk action failed: ' + (r && r.error || 'unknown'));
      }
    }
  }
  ['bulkMarkRemoved','bulkMarkResponded','bulkMarkFailed','bulkRequeue'].forEach((id, i) => {
    const el = document.getElementById(id);
    if (el) el.onclick = () => bulkAction(['mark_removed','mark_responded','mark_failed','requeue'][i]);
  });
  const exportBtn = document.getElementById('exportCsv');
  if (exportBtn) exportBtn.onclick = (e) => { e.preventDefault(); window.location.href = '/takedown/export.csv'; };

  // Compact per-row "⋯" menu — one button per row, opens a popup with
  // the actions. Replaces the previous 4-button row.
  document.addEventListener('click', async (e) => {
    // Toggle the popup when the "⋯" is clicked
    if (e.target.classList && e.target.classList.contains('td-menu-btn')) {
      e.stopPropagation();
      const id = e.target.dataset.id;
      // Close any other open popups first
      document.querySelectorAll('.td-menu-popup').forEach(p => {
        if (p.dataset.id !== id) p.classList.add('hidden');
      });
      const popup = document.querySelector(`.td-menu-popup[data-id="${id}"]`);
      if (popup) popup.classList.toggle('hidden');
      return;
    }
    // Click outside any popup closes all of them
    if (!e.target.closest('.td-menu')) {
      document.querySelectorAll('.td-menu-popup').forEach(p => p.classList.add('hidden'));
    }
    // Action button inside the popup
    if (e.target.classList && e.target.classList.contains('td-act')) {
      const act = e.target.dataset.act;
      const id  = e.target.dataset.id;
      e.target.closest('.td-menu-popup').classList.add('hidden');
      if (act === 'check') {
        if (u.role !== 'superadmin') { alert('Manual health check is superadmin-only.'); return; }
        const r = await api('/takedown/' + id + '/check-now', { method: 'POST' });
        if (r && r.ok) {
          const c = r.check;
          alert('Health: ' + c.status + (c.http_status ? ' (HTTP ' + c.http_status + ')' : '') + '\n' + c.reason);
          location.reload();
        } else alert('Check failed: ' + (r && (r.error || r.message) || 'unknown'));
      } else if (act === 'notes') {
        openNotesModal(id);
      } else if (act === 'removed' || act === 'responded') {
        if (!confirm('Mark takedown #' + id + ' as ' + act + '?')) return;
        const r = await api('/takedown/' + id + '/status', { method: 'POST', body: JSON.stringify({ status: act }) });
        if (r && r.ok) location.reload();
        else alert('Status update failed: ' + (r && (r.error || r.message) || 'unknown'));
      }
    }
  });

  // Backwards-compat shim for the OLD class-based handlers (just in
  // case any cached HTML still has the old button class names).
  document.addEventListener('click', async (e) => {
    if (e.target.classList && e.target.classList.contains('td-check-now')) {
      if (u.role !== 'superadmin') { alert('Manual health check is superadmin-only.'); return; }
      const id = e.target.dataset.id;
      const r = await api('/takedown/' + id + '/check-now', { method: 'POST' });
      if (r && r.ok) location.reload();
      else alert('Check failed: ' + (r && (r.error || r.message) || 'unknown'));
    }
    if (e.target.classList && e.target.classList.contains('td-status-action')) {
      const id = e.target.dataset.id;
      const status = e.target.dataset.status;
      if (!confirm('Mark takedown #' + id + ' as ' + status + '?')) return;
      e.target.disabled = true;
      const r = await api('/takedown/' + id + '/status', {
        method: 'POST',
        body: JSON.stringify({ status }),
      });
      e.target.disabled = false;
      if (r && r.ok) {
        location.reload();
      } else {
        alert('Status update failed: ' + (r && (r.error || r.message) || 'unknown'));
      }
    }
    // Notes modal trigger
    if (e.target.classList && e.target.classList.contains('td-notes')) {
      const id = e.target.dataset.id;
      openNotesModal(id);
    }
  });

  // ---- Operator notes modal ----
  async function openNotesModal(takedownId) {
    const modal = document.getElementById('notesModal');
    const body  = document.getElementById('notesBody');
    const addForm = document.getElementById('notesAddForm');
    if (!modal) return;
    body.innerHTML = '<div class="muted center" style="padding:1rem">Loading…</div>';
    addForm.style.display = u.role === 'superadmin' ? '' : 'none';
    addForm.dataset.takedownId = takedownId;
    modal.classList.remove('hidden');
    try {
      const r = await api('/takedown/' + takedownId + '/notes');
      if (!r) return;
      if (!r.notes.length) {
        body.innerHTML = '<div class="muted center" style="padding:1rem">No notes yet. ' + (u.role === 'superadmin' ? 'Add one below.' : '') + '</div>';
        return;
      }
      body.innerHTML = r.notes.map(n => `
        <div class="note-card" data-id="${n.id}">
          <div class="between">
            <div>
              <strong>${esc(n.author_name || n.author_email || 'unknown')}</strong>
              <span class="muted mono small-text">${new Date(n.created_at).toLocaleString()}${n.updated_at && n.updated_at !== n.created_at ? ' · edited ' + new Date(n.updated_at).toLocaleString() : ''}</span>
            </div>
            ${u.role === 'superadmin' ? `<button class="td-note-delete pill danger" data-id="${n.id}" title="Delete note">×</button>` : ''}
          </div>
          <div class="note-body mt-1">${esc(n.body)}</div>
        </div>
      `).join('');
    } catch (e) {
      body.innerHTML = '<div class="flash err">Failed to load notes.</div>';
    }
  }

  // Submit new note + delete handler
  document.addEventListener('submit', async (e) => {
    if (e.target && e.target.id === 'notesAddForm') {
      e.preventDefault();
      const ta = e.target.body;
      if (!ta || !ta.value.trim()) return;
      const tdId = e.target.dataset.takedownId;
      const r = await api('/takedown/' + tdId + '/notes', { method: 'POST', body: JSON.stringify({ body: ta.value }) });
      if (r && r.ok) { ta.value = ''; openNotesModal(tdId); /* refresh */ }
      else alert('Failed to add note: ' + (r && (r.error || r.message) || 'unknown'));
    }
  });
  document.addEventListener('click', async (e) => {
    if (e.target.classList && e.target.classList.contains('td-note-delete')) {
      const noteId = e.target.dataset.id;
      if (!confirm('Delete this note?')) return;
      const r = await api('/takedown/notes/' + noteId, { method: 'DELETE' });
      if (r && r.ok) {
        const tdId = document.getElementById('notesAddForm').dataset.takedownId;
        openNotesModal(tdId);
      } else alert('Delete failed: ' + (r && (r.error || r.message) || 'unknown'));
    }
  });

  // ---- logout ----
  const lo = document.getElementById('logout');
  if (lo) lo.onclick = async (e) => { e.preventDefault(); await api('/auth/logout', { method: 'POST' }); location.href = '/login'; };

  // Notes modal close
  const notesClose = document.getElementById('notesClose');
  if (notesClose) notesClose.onclick = () => document.getElementById('notesModal').classList.add('hidden');
})();
