/* === Media upload (admin) — Google Alert emails → mention entities ===
   Drop saved alert emails (.eml — Outlook web "Download message", or drag
   the message to the desktop from OWA). The alert's plain-text part is
   rigidly structured:

       NEWS
       <headline> <https://www.google.com/url?...&url=REAL_URL...>
       <publication name>
       <snippet…>

   so everything parses locally — no AI service, nothing leaves the machine.
   Outlook safelinks wrapping and quoted-printable encoding are undone; the
   real article URL is unwrapped from Google's redirect. Geography is
   inferred from state names (same rule as the Salesforce meetings import)
   and every row is reviewable before sync. Re-importing the same alert is
   a no-op: mention ids derive from the article URL. */

function muDecodeQP(s) {
  // Quoted-printable → UTF-8 (soft line breaks removed, =XX bytes decoded).
  const joined = s.replace(/=\r?\n/g, "");
  const bytes = [];
  for (let i = 0; i < joined.length; i++) {
    if (joined[i] === "=" && /^[0-9A-Fa-f]{2}$/.test(joined.slice(i + 1, i + 3))) {
      bytes.push(parseInt(joined.slice(i + 1, i + 3), 16)); i += 2;
    } else {
      bytes.push(joined.charCodeAt(i) & 0xff);
    }
  }
  try { return new TextDecoder("utf-8").decode(new Uint8Array(bytes)); }
  catch (e) { return joined; }
}

function muUnwrapUrl(u) {
  let url = u;
  // Outlook safelinks first, then Google's redirect.
  let m = url.match(/safelinks\.protection\.outlook\.com\/\?url=([^&]+)/i);
  if (m) { try { url = decodeURIComponent(m[1]); } catch (e) {} }
  m = url.match(/google\.com\/url\?[^ >]*?[?&]url=([^&>]+)/i);
  if (m) { try { url = decodeURIComponent(m[1]); } catch (e) {} }
  return url;
}

/* One .eml → { topic, date, items: [{title, url, publication, snippet}] } */
function parseAlertEml(raw) {
  // Prefer the text/plain MIME part; fall back to a tag-stripped whole body.
  // The end-lookahead requires a real MIME boundary (long token), so the
  // "---------- Forwarded message ---------" divider doesn't truncate it.
  let body = raw;
  const plain = raw.match(/Content-Type: text\/plain[^]*?\r?\n\r?\n([^]*?)(?=\r?\n--[0-9a-zA-Z_'()+,./:=?-]{12,}(?:--)?\s*\r?\n|$)/i);
  if (plain) body = plain[1];
  body = muDecodeQP(body);
  if (!plain) body = body.replace(/<style[^]*?<\/style>/gi, "").replace(/<[^>]+>/g, "\n");

  const topicM = body.match(/Google Alert - (.+)/) || raw.match(/Google Alert - (.+)/);
  const topic = topicM ? topicM[1].replace(/["\r]/g, "").trim() : "";
  // "Daily update ⋅ May 20, 2017" — the alert's own date, not the forward's.
  const dateM = body.match(/(?:Daily|Weekly|As-it-happens) update[^\w]*([A-Z][a-z]+ \d{1,2}, \d{4})/);
  const alertDate = dateM ? dateM[1] : "";

  const lines = body.split(/\r?\n/).map((l) => l.trim());
  const items = [];
  for (let i = 0; i < lines.length; i++) {
    const m = lines[i].match(/^(.*?)\s*<(https?:\/\/[^>]*google\.com\/url[^>]*)>\s*$/) ||
              lines[i].match(/^(.*?)\s*<(https?:\/\/[^>]*safelinks[^>]*)>\s*$/i);
    if (!m || !m[1]) continue;
    const title = m[1].trim();
    if (/^(\[Google\]|Google Alert|Flag as|See more|Unsubscribe|RSS|Receive|Send Feedback)/i.test(title)) continue;
    const url = muUnwrapUrl(m[2]);
    if (!/^https?:\/\//.test(url) || /google\.com\/alerts/.test(url)) continue;
    // Next non-empty line = publication; following lines to a blank = snippet.
    let j = i + 1;
    while (j < lines.length && !lines[j]) j++;
    const publication = (lines[j] || "").replace(/<[^>]*>/g, "").trim();
    let snippet = [];
    for (let k = j + 1; k < lines.length && lines[k]; k++) {
      if (/^(Flag as|Facebook|Twitter|See more)/i.test(lines[k])) break;
      snippet.push(lines[k]);
    }
    items.push({ title, url, publication, snippet: snippet.join(" ").replace(/\s+/g, " ").trim() });
    i = j;
  }
  return { topic, date: alertDate, items };
}

function MediaUpload({ onClose }) {
  const [rows, setRows] = React.useState([]);
  const [err, setErr] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [done, setDone] = React.useState(null);
  const [users, setUsers] = React.useState([]);
  React.useEffect(() => { window.Store.listUsers().then(setUsers).catch(() => setUsers([])); }, []);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  function handleFiles(e) {
    setErr(null); setDone(null);
    for (const f of [...e.target.files]) {
      const reader = new FileReader();
      reader.onload = () => {
        try {
          const { topic, date, items } = parseAlertEml(String(reader.result));
          if (!items.length) { setErr(`"${f.name}": no alert items found — is it a saved Google Alert email (.eml)?`); return; }
          const existing = new Set((window.MENTIONS || []).map((m) => m.id));
          const dismissed = new Set((window.META && window.META.dismissedMentions) || []);
          setRows((prev) => {
            const seen = new Set(prev.map((r) => r.id));
            const add = items.map((it) => {
              const id = outSlugUrl(it.url);
              const prevSkipped = dismissed.has(id);
              return {
                id,
                include: !prevSkipped, quoted: false, who: "", workIds: [],
                url: it.url, title: it.title, publication: it.publication,
                summary: it.snippet, topic,
                date: date ? new Date(date).toISOString().slice(0, 10) : "",
                state: sfInferState([it.title, it.snippet, it.publication]),
                pubType: "",
                already: existing.has(id),
                prevSkipped,
              };
            }).filter((r) => !seen.has(r.id));
            return [...prev, ...add];
          });
        } catch (ex) { setErr(`${f.name}: ${String(ex.message || ex)}`); }
      };
      reader.readAsText(f, "utf-8");
    }
    e.target.value = "";
  }

  const setRow = (i, patch) => setRows(rows.map((r, x) => x === i ? { ...r, ...patch } : r));
  const toSync = rows.filter((r) => r.include && !r.already);
  // Unchecked rows get REMEMBERED: next time the same URL shows up (re-upload
  // or a fresh alert), it arrives unchecked with a "skipped before" tag.
  const dismissedNow = (window.META && window.META.dismissedMentions) || [];
  const toDismiss = rows.filter((r) => !r.include && !r.already && !dismissedNow.includes(r.id));

  async function commit() {
    setBusy(true); setErr(null);
    try {
      const events = toSync.map((r) => {
        const { include, already, prevSkipped, ...data } = r;
        return { entityType: "mention", entityId: r.id, action: "create", patch: data, note: "Google Alert import" };
      });
      events.push({ entityType: "meta", entityId: "app", action: "update",
        patch: {
          lastMediaImport: new Date().toISOString(),
          ...(toDismiss.length ? { dismissedMentions: [...new Set([...dismissedNow, ...toDismiss.map((r) => r.id)])] } : {}),
        },
        prev: { lastMediaImport: (window.META && window.META.lastMediaImport) || null } });
      for (let i = 0; i < events.length; i += 40) await window.Store.commit(events.slice(i, i + 40));
      setDone(`Imported ${toSync.length} mention(s)${toDismiss.length ? ` · will skip ${toDismiss.length} next time` : ""}.`);
      setRows([]);
    } catch (ex) { setErr(String(ex.message || ex)); }
    finally { setBusy(false); }
  }

  const input = {
    width: "100%", padding: "4px 6px", borderRadius: 4, border: "1px solid var(--border)",
    background: "var(--surface-2)", font: "500 11px/1.3 var(--sans)", outline: "none",
  };

  return (
    <>
      <div className="scrim is-open" onClick={onClose}></div>
      <aside className="side-panel is-open" style={{ width: 700 }}>
        <div className="side-panel__head">
          <button className="side-panel__close" onClick={onClose} title="Close">
            <svg width="14" height="14" viewBox="0 0 14 14"><path d="M3 3 L11 11 M11 3 L3 11" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" /></svg>
          </button>
          <div className="side-panel__eyebrow">Media sync</div>
          <div className="side-panel__title" style={{ fontSize: 19 }}>Import Google Alert emails</div>
        </div>

        <div className="side-panel__section">
          <div className="muted" style={{ fontSize: 12, lineHeight: 1.6, marginBottom: 10 }}>
            Save alert emails as <strong>.eml</strong> (Outlook web: ⋯ → Download; or drag the message to your
            desktop) and drop them here — several at once is fine. Everything parses locally; review each row,
            fix geography/type, untick anything irrelevant, then sync. Already-tracked articles are skipped.
          </div>
          <input type="file" accept=".eml,.txt" multiple onChange={handleFiles}
            style={{ fontSize: 12, fontFamily: "var(--mono)" }} />
        </div>

        <datalist id="mu-people">
          {users.map((u) => <option key={u.id} value={u.name} />)}
        </datalist>

        {rows.length ? (
          <div className="side-panel__section">
            <h4>Review before sync — {toSync.length} of {rows.length} selected</h4>
            {rows.map((r, i) => (
              <div key={r.id} style={{ padding: "8px 0", borderTop: "1px solid var(--rule)", opacity: r.already ? 0.5 : 1 }}>
                <label className="row" style={{ gap: 6, alignItems: "flex-start", cursor: r.already ? "default" : "pointer" }}>
                  <input type="checkbox" checked={r.include && !r.already} disabled={r.already}
                    onChange={(e) => setRow(i, { include: e.target.checked })} style={{ marginTop: 3 }} />
                  <span style={{ fontSize: 12, flex: 1 }}>
                    <strong>{r.title}</strong>
                    {r.already ? <span className="chip" style={{ marginLeft: 6, fontSize: 10 }}>already tracked</span> : null}
                    {!r.already && r.prevSkipped ? <span className="chip" style={{ marginLeft: 6, fontSize: 10 }} title="You skipped this article in an earlier import — tick it to import anyway.">skipped before</span> : null}
                    <span className="muted" style={{ display: "block", fontSize: 11 }}>
                      {r.publication || "publication?"} · {r.date || "date?"} · {r.summary.slice(0, 140)}{r.summary.length > 140 ? "…" : ""}
                    </span>
                  </span>
                </label>
                {!r.already ? (
                  <div style={{ display: "grid", gridTemplateColumns: "90px 1fr 1fr auto 90px", gap: 6, marginTop: 6, marginLeft: 22 }}>
                    <select style={input} value={r.state} onChange={(e) => setRow(i, { state: e.target.value })}>
                      <option value="">National</option>
                      {Object.keys(window.STATE_NAMES).sort().map((a) => <option key={a} value={a}>{a}</option>)}
                    </select>
                    <select style={input} value={r.pubType} onChange={(e) => setRow(i, { pubType: e.target.value })}>
                      <option value="">— publication type</option>
                      {window.PUB_TYPES.map((t) => <option key={t} value={t}>{t}</option>)}
                    </select>
                    <select style={input} value={(r.workIds || [])[0] || ""} title="Link the tracked work product this article covers — its owner gets Activity-snapshot credit"
                      onChange={(e) => setRow(i, { workIds: e.target.value ? [e.target.value] : [] })}>
                      <option value="">— link work product</option>
                      {(window.WORK_ITEMS || []).map((w) => <option key={w.id} value={w.id}>{w.id} — {w.title.slice(0, 50)}</option>)}
                    </select>
                    <label className="row" style={{ gap: 4, fontSize: 11, color: "var(--ink-2)", cursor: "pointer", userSelect: "none" }}>
                      <input type="checkbox" checked={r.quoted} onChange={(e) => setRow(i, { quoted: e.target.checked })} />
                      quoted
                    </label>
                    <input style={input} value={r.who} placeholder="who?" list="mu-people"
                      onChange={(e) => setRow(i, { who: e.target.value, quoted: r.quoted || !!e.target.value.trim() })} />
                  </div>
                ) : null}
              </div>
            ))}
            <button className="btn btn--primary" style={{ marginTop: 12 }} disabled={busy || (toSync.length === 0 && toDismiss.length === 0)} onClick={commit}>
              {busy ? "Importing…" :
               toSync.length === 0 && toDismiss.length === 0 ? "Nothing to import" :
               `${toSync.length ? `Import ${toSync.length} mention(s)` : "Import nothing"}${toDismiss.length ? ` · remember ${toDismiss.length} skipped` : ""}`}
            </button>
          </div>
        ) : null}

        {err ? <div style={{ margin: "14px 22px", padding: "8px 12px", borderRadius: 4, background: "var(--red-bg)", color: "var(--red)", fontSize: 12 }}>{err}</div> : null}
        {done ? <div style={{ margin: "14px 22px", padding: "8px 12px", borderRadius: 4, background: "var(--green-bg)", color: "var(--green)", fontSize: 12 }}>{done}</div> : null}
      </aside>
    </>
  );
}

Object.assign(window, { MediaUpload, parseAlertEml });
