<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <meta name="theme-color" content="#0EA5E9" />
  <title>Takedowns — takedowns</title>
  <link rel="stylesheet" href="/public/css/glass.css?v=2-light" />
  <style>
    .chain-step { display:flex; gap:.6rem; align-items:flex-start; padding:.55rem .75rem; border-left:3px solid var(--border); margin-bottom:.4rem; background:var(--bg-3); border-radius:0 6px 6px 0; }
    .chain-step.curated { border-left-color: var(--ok); }
    .chain-step.hosting { border-left-color: var(--accent); }
    .chain-step.registrar { border-left-color: var(--warn); }
    .chain-step.icann { border-left-color: var(--danger); }
    .chain-step .num { font-weight:700; min-width:24px; }
    .chain-step .body { flex:1; }
    .chain-step .body .addr { font-family:var(--mono); font-size:.85rem; }
    .chain-step .body .why { font-size:.8rem; color:var(--fg-2); margin-top:.2rem; }
    .job-row { display:flex; gap:.5rem; align-items:center; padding:.5rem .75rem; border-bottom:1px solid var(--border); }
    .job-row .grow { flex:1; min-width:0; }
    .job-row .progress { font-size:.8rem; color:var(--fg-2); }
    .spinner { display:inline-block; width:12px; height:12px; border:2px solid var(--border); border-top-color:var(--accent); border-radius:50%; animation:spin 1s linear infinite; vertical-align:middle; }
    @keyframes spin { to { transform: rotate(360deg); } }
    .pulse { animation: pulse 1.5s ease-in-out infinite; }
    @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
  </style>
</head>
<body>
  <div class="app">
    <aside>
      <a href="/" class="brand" aria-label="takedowns home"><img src="/public/og/logo-mark.png" alt="" width="28" height="28" /><span class="brand-wordmark">takedowns<small>by vesamuni</small></span></a>
      <nav>
        <a href="/dashboard/">Dashboard</a>
        <a href="/cases/">My cases</a>
        <a href="/scan/">Run a scan</a>
        <a href="/takedown/" class="active">Takedowns</a>
        <a href="/billing/">Billing &amp; plan</a>
        <a href="/admin/" data-admin-only class="hidden">Admin panel</a>
        <a href="#" id="logout">Sign out</a>
      </nav>
    </aside>
    <main>
      <header class="topbar">
        <div>
          <h1>Takedowns</h1>
          <div class="muted">DMCA notices to platforms, with automatic 30-hour escalation to hosting, registrar, and ICANN.</div>
        </div>
        <div class="user-pill">
          <span id="siteHealthDot" class="site-health-dot unknown" title="checking…"></span>
        </div>
      </header>
      <div id="flash"></div>

      <!-- Send new takedown -->
      <section class="panel">
        <h2>Send a takedown</h2>
        <form id="tdForm">
          <div class="row">
            <div class="field" style="flex:1">
              <label>Case</label>
              <select id="caseSel" name="case_id" required></select>
            </div>
            <div class="field" style="flex:2">
              <label>URL of the content to remove</label>
              <input id="url" name="url" type="url" required placeholder="https://example.com/post/123" />
            </div>
          </div>
          <div class="row">
            <div class="field" style="flex:1">
              <label>Draft method</label>
              <select name="draft_method">
                <option value="auto">Auto (template for known platforms, AI otherwise)</option>
                <option value="template">Force template</option>
                <option value="ai">Force AI draft</option>
              </select>
            </div>
            <div class="field" style="flex:1; align-self:end">
              <button type="submit">Send takedown</button>
              <button type="button" class="secondary" id="previewBtn">Preview first</button>
            </div>
          </div>
        </form>
      </section>

      <!-- Live jobs (queued + processing) -->
      <section class="panel">
        <div class="between">
          <h2>Active jobs <span id="jobCount" class="muted"></span></h2>
          <div class="muted" id="lastUpdate"></div>
        </div>
        <div id="jobsBlock">
          <div class="muted center" style="padding:1rem">No active jobs.</div>
        </div>
      </section>

      <!-- Preview modal -->
      <div id="previewModal" class="hidden" style="position:fixed;inset:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;z-index:99;overflow:auto;padding:2rem">
        <div class="panel" style="width:100%;max-width:720px">
          <h2>Takedown preview</h2>
          <div id="previewBlock">loading…</div>
          <div class="row mt-2">
            <button id="previewSend">Send this takedown</button>
            <button type="button" class="secondary" id="previewClose">Cancel</button>
          </div>
        </div>
      </div>

      <!-- History (resolved) -->
      <section class="panel">
        <h2>History</h2>
        <table class="tight">
          <thead><tr><th>ID</th><th>URL</th><th>Platform</th><th>Method</th><th>Status</th><th>Sent</th><th>Next escalation</th><th></th></tr></thead>
          <tbody id="rows"></tbody>
        </table>
      </section>
    </main>
  </div>

  <script>
    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;}return r.json().catch(()=>({}));}
    function esc(s){return String(s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));}
    function flash(t,m){document.getElementById('flash').innerHTML=`<div class="flash ${esc(t)}">${esc(m)}</div>`;setTimeout(()=>document.getElementById('flash').innerHTML='',6000);}
    function statusPill(s){return `<span class="pill ${s==='sent'||s==='responded'||s==='removed'?'ok':s==='failed'||s==='cancelled'?'danger':s==='manual_required'?'warn':'info'}">${s}</span>`;}
    function jobPill(s){return `<span class="pill ${s==='sent'?'ok':s==='failed'?'danger':s==='manual_required'?'warn':s==='cancelled'?'danger':'info pulse'}">${s}</span>`;}

    function chainHtml(chain) {
      if (!chain || !chain.length) return '<div class="muted">No escalation chain built.</div>';
      return chain.map(s => `
        <div class="chain-step ${esc(s.type)}">
          <div class="num">${s.step}.</div>
          <div class="body">
            <div><span class="pill info">${esc(s.type)}</span> <span class="addr">${esc(s.address)}</span></div>
            <div class="why">${esc(s.why || '')}</div>
            <div class="muted" style="font-size:.7rem">source: ${esc(s.source || '')}</div>
          </div>
        </div>`).join('');
    }

    function progressForJob(j) {
      const map = {
        queued:        'Queued — worker will pick this up shortly',
        discovering:   'Looking up contacts and hosting…',
        drafting:      'Drafting the email…',
        dispatching:   'Sending via Resend…',
        sent:          'Sent successfully',
        manual_required: 'No direct email found — manual action required',
        failed:        j.last_error ? `Failed: ${j.last_error}` : 'Failed',
        cancelled:     'Cancelled',
        escalated:     'Escalated to next step in chain',
      };
      return j.progress || map[j.status] || j.status;
    }

    async function loadJobs() {
      const d = await api('/takedown/jobs');
      if (!d) return;
      const active = d.jobs.filter(j => !['sent','manual_required','cancelled'].includes(j.status) || (Date.now() - j.updated_at) < 60*60*1000);
      const block = document.getElementById('jobsBlock');
      if (!active.length) { block.innerHTML = '<div class="muted center" style="padding:1rem">No active jobs.</div>'; }
      else block.innerHTML = active.map(j => `
        <div class="job-row" data-id="${j.id}">
          <div>${jobPill(j.status)}</div>
          <div class="grow">
            <div><a href="${esc(j.url)}" target="_blank" rel="noopener">${esc(j.url).slice(0,80)}</a></div>
            <div class="progress"><span class="spinner"></span> ${esc(progressForJob(j))} <span class="muted">— attempt ${j.attempts}/${j.max_attempts}</span></div>
          </div>
          <div class="flex">
            ${j.status === 'failed' ? `<button class="secondary" data-act="retry" data-id="${j.id}">Retry</button>` : ''}
            ${['queued','failed','discovering','drafting','dispatching'].includes(j.status) ? `<button class="secondary" data-act="cancel" data-id="${j.id}">Cancel</button>` : ''}
            <button class="secondary" data-act="view" data-id="${j.id}">View</button>
          </div>
        </div>`).join('');
      document.getElementById('jobCount').textContent = `(${active.length} active)`;
      document.getElementById('lastUpdate').textContent = 'updated ' + new Date().toLocaleTimeString();
      // Bind actions
      document.querySelectorAll('[data-act]').forEach(b => b.onclick = () => actJob(b.dataset.act, b.dataset.id));
    }

    async function actJob(act, id) {
      if (act === 'view') return viewJob(id);
      if (act === 'retry') { await api(`/takedown/job/${id}/retry`, { method: 'POST' }); flash('ok', 'Re-queued.'); loadJobs(); }
      if (act === 'cancel') { if (!confirm('Cancel this job?')) return; await api(`/takedown/job/${id}/cancel`, { method: 'POST' }); flash('info', 'Cancelled.'); loadJobs(); }
    }

    async function viewJob(id) {
      const d = await api(`/takedown/job/${id}`);
      if (!d) return;
      const j = d.job;
      let html = `<h3>Job #${j.id}</h3>`;
      html += `<div>${jobPill(j.status)} <span class="muted">attempt ${j.attempts}/${j.max_attempts}</span></div>`;
      html += `<div class="mt-1"><b>URL:</b> <a href="${esc(j.url)}" target="_blank" rel="noopener">${esc(j.url)}</a></div>`;
      html += `<div><b>Progress:</b> ${esc(progressForJob(j))}</div>`;
      if (j.last_error) html += `<div class="muted" style="color:var(--danger)"><b>Last error:</b> ${esc(j.last_error)}</div>`;
      if (d.escalation && d.escalation.length) {
        html += `<h4 class="mt-2">Escalation chain</h4>${chainHtml(d.escalation)}`;
      }
      if (d.result && d.result.takedown) {
        html += `<h4 class="mt-2">Result</h4>`;
        html += `<div>Contact: <span class="mono">${esc(d.result.contact_email || '(none)')}</span></div>`;
        html += `<div>Method: ${esc(d.result.method || '—')}</div>`;
        if (d.result.action_required) {
          html += `<h4 class="mt-2">Action required</h4><pre style="background:var(--bg-3);padding:.5rem;border-radius:6px;overflow:auto">${esc(JSON.stringify(d.result.action_required, null, 2))}</pre>`;
        }
        if (d.result.takedown.email_body) {
          html += `<h4 class="mt-2">Email sent</h4><pre style="background:var(--bg-3);padding:.5rem;border-radius:6px;overflow:auto;max-height:240px">${esc(d.result.takedown.email_body)}</pre>`;
        }
      }
      document.getElementById('previewBlock').innerHTML = html;
      document.getElementById('previewSend').style.display = 'none';
      document.getElementById('previewModal').classList.remove('hidden');
    }

    async function loadHistory() {
      const d = await api('/takedown/');
      if (!d) return;
      const rows = d.takedowns.map(t => {
        let chainShort = '';
        try {
          const ch = t.escalation_json ? JSON.parse(t.escalation_json) : null;
          if (ch && ch.length) chainShort = ch.slice(0,3).map(s => s.address).join(' → ') + (ch.length > 3 ? ' → …' : '');
        } catch {}
        return `<tr>
          <td>${t.id}</td>
          <td><a href="${esc(t.url)}" target="_blank" rel="noopener">${esc(t.url).slice(0,50)}…</a></td>
          <td>${esc(t.domain)}</td>
          <td><span class="pill info">${esc(t.draft_method)}</span></td>
          <td>${statusPill(t.status)}</td>
          <td class="muted mono">${t.sent_at ? new Date(t.sent_at).toLocaleString() : '—'}</td>
          <td class="muted" style="font-size:.75rem">${chainShort ? esc(chainShort) : '—'}</td>
          <td>
            <select data-id="${t.id}" class="statusSel">
              ${['queued','sent','opened','responded','removed','rejected','failed'].map(s => `<option ${s===t.status?'selected':''} value="${s}">${s}</option>`).join('')}
            </select>
          </td>
        </tr>`;
      }).join('') || `<tr><td colspan="8" class="muted center">No takedowns yet.</td></tr>`;
      document.getElementById('rows').innerHTML = rows;
      document.querySelectorAll('.statusSel').forEach(sel => sel.onchange = async (e) => {
        const id = sel.dataset.id;
        const r = await api(`/takedown/${id}/status`, { method: 'POST', body: JSON.stringify({ status: sel.value }) });
        if (r && r.ok) flash('ok', 'Status updated.'); else flash('err', 'Could not update.');
      });
    }

    async function loadCases() {
      const d = await api('/cases/');
      if (!d) return;
      const sel = document.getElementById('caseSel');
      sel.innerHTML = '<option value="">— pick a case —</option>' + d.cases.map(c => `<option value="${c.id}">#${c.id} — ${esc(c.victim_name)}</option>`).join('');
      const params = new URLSearchParams(location.search);
      if (params.get('case_id')) sel.value = params.get('case_id');
      if (params.get('url'))     document.getElementById('url').value = params.get('url');
    }

    document.getElementById('tdForm').onsubmit = async (e) => {
      e.preventDefault();
      const body = Object.fromEntries(new FormData(e.target));
      if (body.draft_method === 'auto') delete body.draft_method;
      const r = await api('/takedown/', { method: 'POST', body: JSON.stringify(body) });
      if (r && r.ok) {
        flash('ok', r.message);
        loadJobs();
        e.target.reset();
      } else flash('err', (r && (r.message||r.error)) || 'Failed');
    };

    document.getElementById('previewBtn').onclick = async () => {
      const caseId = document.getElementById('caseSel').value;
      const url    = document.getElementById('url').value;
      if (!caseId || !url) return flash('err', 'Pick a case and enter a URL first.');
      const r = await api(`/takedown/preview?case_id=${encodeURIComponent(caseId)}&url=${encodeURIComponent(url)}`);
      if (!r || !r.ok) return flash('err', (r && (r.message||r.error)) || 'Preview failed');
      let html = '';
      if (r.contact) {
        html += `<div><b>Will email:</b> <span class="mono">${esc(r.contact)}</span></div>`;
        html += `<div><b>Subject:</b> ${esc(r.subject)}</div>`;
        html += `<h4 class="mt-2">Body</h4><pre style="background:var(--bg-3);padding:.5rem;border-radius:6px;max-height:300px;overflow:auto">${esc(r.body)}</pre>`;
      } else {
        html += `<div class="flash warn">No direct abuse email was found for this domain.</div>`;
        if (r.security) html += `<div class="mt-1"><b>Security disclosure program:</b> <a href="${esc(r.security)}" target="_blank">${esc(r.security)}</a></div>`;
        if (r.form_url) html += `<div><b>Takedown form:</b> <a href="${esc(r.form_url)}" target="_blank">${esc(r.form_url)}</a></div>`;
        if (r.reason) html += `<div class="muted">${esc(r.reason)}</div>`;
      }
      if (r.escalation_chain && r.escalation_chain.length) {
        html += `<h4 class="mt-2">Escalation chain (${r.escalation_chain.length} steps)</h4>${chainHtml(r.escalation_chain)}`;
      }
      document.getElementById('previewBlock').innerHTML = html;
      document.getElementById('previewSend').style.display = r.contact ? '' : 'none';
      document.getElementById('previewModal').classList.remove('hidden');
    };

    document.getElementById('previewClose').onclick = () => document.getElementById('previewModal').classList.add('hidden');
    document.getElementById('previewSend').onclick = () => document.getElementById('tdForm').dispatchEvent(new Event('submit', { cancelable: true }));

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

    // Poll for live job updates every 3s while there are active jobs
    let pollHandle = null;
    function startPolling() {
      if (pollHandle) return;
      pollHandle = setInterval(() => {
        loadJobs();
      }, 3000);
    }
    function stopPolling() {
      if (pollHandle) { clearInterval(pollHandle); pollHandle = null; }
    }

    loadCases().then(loadJobs).then(loadHistory);
    startPolling();
  </script>
  <script src="/public/js/siteHealth.js" defer></script>
</body>
</html>
