/* Transcripts page data binder.
 * Replaces the design's demo .tcard rows with the user's real dictation
 * history from ~/Library/Application Support/Lirelle/transcripts/index.json.
 *
 * Wires:
 *   - .seg button click → filter by cleanup level (raw/light/standard/polished)
 *   - .tcard [title="Copy"] click → copy that transcript's text to the clipboard
 *   - .tcard [title="Delete"] click → DISABLED. Per GLOBAL RULE #1 in
 *     OPS/CLAUDE.md, no agent may delete transcripts without explicit
 *     per-item user confirmation. The trash icon is left in place so the
 *     UI matches the design, but the click is a no-op. We'll wire a
 *     proper confirm modal in a later phase.
 *   - .tcard [title="Rewrite"] click → DISABLED for now (Phase F).
 *   - .search input → simple substring filter (client-side).
 */

(function () {
  function esc(s) {
    return String(s == null ? "" : s)
      .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
  }

  var ICON_COPY =
    '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">'
    + '<path d="M9 9h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1zM5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1" '
    + 'stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>';
  var ICON_REWRITE =
    '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">'
    + '<path d="M4 20h4l10.5-10.5a2 2 0 0 0-2.8-2.8L5 17v3zM13.5 6.5l4 4" '
    + 'stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>';
  var ICON_DELETE =
    '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">'
    + '<path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13" '
    + 'stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>';

  function buildCard(t) {
    var card = document.createElement("div");
    card.className = "tcard";
    card.dataset.id = t.id;
    card.dataset.cleanup = t.cleanup;
    card.dataset.text = t.text;
    card.innerHTML =
      '<div class="tc-text">' + esc(t.text) + "</div>"
      + '<div class="tc-foot">'
        + '<span class="tc-date">' + esc(t.date) + "</span>"
        + '<span class="badge summary"><span class="bdot"></span>'
          + esc(t.badge_label) + "</span>"
        + '<div class="tc-actions">'
          + '<button class="tool-btn icon-only" title="Copy" data-act="copy">'
            + ICON_COPY + "</button>"
          // Rewrite + Delete kept visually but disabled — see header comment.
          + '<button class="tool-btn icon-only" title="Rewrite (soon)" '
          + 'data-act="rewrite" disabled style="opacity:0.35; cursor:not-allowed;">'
            + ICON_REWRITE + "</button>"
          + '<button class="tool-btn icon-only" title="Delete (disabled — '
          + 'will be re-enabled with a confirm step)" data-act="delete" '
          + 'disabled style="opacity:0.35; cursor:not-allowed;">'
            + ICON_DELETE + "</button>"
        + "</div>"
      + "</div>";
    return card;
  }

  // Current filter level — mirrors the .seg .on button.
  var currentLevel = "standard";
  var allTranscripts = [];

  function getActiveLevel() {
    var on = document.querySelector(".seg .on, .seg button.on");
    if (!on) return "standard";
    return on.textContent.trim().toLowerCase();
  }

  function applyFilter() {
    currentLevel = getActiveLevel();
    var search = (document.querySelector(".search input") || {}).value || "";
    search = search.trim().toLowerCase();

    var container = document.querySelector(".sec-wrap");
    if (!container) return;

    // Drop existing cards AND any previous empty placeholder, so we don't
    // accumulate stale nodes when the filter changes.
    container.querySelectorAll(".tcard, .tcard-empty").forEach(function (n) {
      n.parentNode.removeChild(n);
    });

    var cleanupCounts = {};
    allTranscripts.forEach(function (t) {
      cleanupCounts[t.cleanup] = (cleanupCounts[t.cleanup] || 0) + 1;
    });
    console.log("[transcripts-bind] applyFilter level=" + currentLevel
                + " total=" + allTranscripts.length
                + " counts=" + JSON.stringify(cleanupCounts));

    var filtered = allTranscripts.filter(function (t) {
      if (currentLevel !== "all" && t.cleanup !== currentLevel) return false;
      if (search && t.text.toLowerCase().indexOf(search) < 0) return false;
      return true;
    });
    console.log("[transcripts-bind] filtered=" + filtered.length);

    if (filtered.length === 0) {
      var empty = document.createElement("div");
      empty.className = "tcard-empty";
      empty.style.cssText =
        "padding: 32px 4px; text-align: center; "
        + "font-family: var(--mono); font-size: 12px; "
        + "color: var(--ink-dim); line-height: 1.7;";
      var hasOtherLevels = allTranscripts.length > 0;
      if (hasOtherLevels) {
        empty.innerHTML =
          "No transcripts at <b>" + esc(currentLevel) + "</b> level. "
          + "Your other levels: "
          + Object.keys(cleanupCounts)
              .map(function (k) {
                return '<span style="color:var(--accent,#ebb638);'
                  + 'cursor:pointer;text-decoration:underline;" '
                  + 'data-jump-level="' + esc(k) + '">'
                  + esc(k) + " (" + cleanupCounts[k] + ")</span>";
              })
              .join(" · ");
      } else {
        empty.innerHTML = "No transcripts yet.<br>"
          + 'Hold <kbd style="font-family:var(--mono);background:rgba(255,255,255,.06);'
          + 'border:1px solid rgba(255,255,255,.08);padding:1px 6px;border-radius:4px;">⌥</kbd> '
          + "to dictate one.";
      }
      container.appendChild(empty);
      return;
    }

    // Batch the inserts into a DocumentFragment — one reflow instead of
    // one per card. With 40+ Raw cards the per-card reflow looked like
    // the page was jumping.
    var frag = document.createDocumentFragment();
    filtered.forEach(function (t) { frag.appendChild(buildCard(t)); });
    container.appendChild(frag);

    // Reset scroll-to-top so switching from an empty tab to a populated
    // one doesn't land you halfway down the list. The scrollable element
    // is .detail-scroll (the outer container above .sec-wrap).
    var scroller = document.querySelector(".detail-scroll");
    if (scroller) scroller.scrollTop = 0;
  }

  async function loadTranscripts() {
    console.log("[transcripts-bind] loading…");
    try {
      allTranscripts = await window.lumo.call("requestTranscripts");
    } catch (err) {
      console.error("[transcripts-bind] requestTranscripts failed:", err);
      allTranscripts = [];
    }
    if (!Array.isArray(allTranscripts)) allTranscripts = [];
    console.log("[transcripts-bind] got", allTranscripts.length, "transcripts");
    applyFilter();
  }

  function wireSegments() {
    document.querySelectorAll(".seg button").forEach(function (btn) {
      btn.addEventListener("click", function () {
        document.querySelectorAll(".seg button").forEach(function (b) {
          b.classList.toggle("on", b === btn);
        });
        applyFilter();
      });
    });
  }

  function wireSearch() {
    var input = document.querySelector(".search input");
    if (!input) return;
    var t = null;
    input.addEventListener("input", function () {
      if (t) clearTimeout(t);
      t = setTimeout(applyFilter, 120);
    });
  }

  function wireCardActions() {
    // Event delegation on the .sec-wrap container, since cards are rebuilt
    // on every filter change.
    var container = document.querySelector(".sec-wrap");
    if (!container) return;
    container.addEventListener("click", function (e) {
      // Jump-to-level shortcut from the empty placeholder.
      var jump = e.target.closest && e.target.closest("[data-jump-level]");
      if (jump) {
        var level = jump.dataset.jumpLevel;
        document.querySelectorAll(".seg button").forEach(function (b) {
          b.classList.toggle("on", b.textContent.trim().toLowerCase() === level);
        });
        applyFilter();
        return;
      }
      var btn = e.target.closest && e.target.closest("button[data-act]");
      if (!btn) return;
      var act = btn.dataset.act;
      var card = btn.closest(".tcard");
      if (!card) return;
      if (act === "copy") {
        var text = card.dataset.text || "";
        window.lumo.call("copyTranscript", { text: text }).then(function (res) {
          if (res && res.ok) {
            // Brief visual confirm — flash the button background.
            btn.style.color = "var(--accent, #ebb638)";
            setTimeout(function () { btn.style.color = ""; }, 700);
          } else {
            console.warn("[transcripts-bind] copy failed:", res);
          }
        }).catch(function (err) {
          console.error("[transcripts-bind] copy threw:", err);
        });
      }
      // "rewrite" and "delete" are intentionally inert — see header comment.
    });
  }

  async function setInitialActiveLevel() {
    // Default-tab fix: the design HTML hard-codes Standard as the .on
    // segment, but the user's cleanup level may be raw / light / etc.
    // Read the config-side default and align the .on button before the
    // first filter pass so the user lands on the tab where their
    // dictations actually live.
    var level = "raw";
    try {
      var v = await window.lumo.call("requestUserCleanupLevel");
      if (typeof v === "string" && v) level = v;
    } catch (e) {
      console.warn("[transcripts-bind] requestUserCleanupLevel failed:", e);
    }
    document.querySelectorAll(".seg button").forEach(function (b) {
      b.classList.toggle("on", b.textContent.trim().toLowerCase() === level);
    });
  }

  function init() {
    wireSegments();
    wireSearch();
    wireCardActions();
    // Order: set active tab BEFORE the first filter so we don't render
    // the wrong (Standard) level once and then re-render. setInitial...
    // is async; loadTranscripts runs after it resolves.
    setInitialActiveLevel().then(loadTranscripts);

    // Refresh on window focus — covers the common case where the user
    // dictated something while the Transcripts page was already open
    // (the index.json got a new entry but our cached allTranscripts
    // didn't). visibilitychange fires when the WebView's containing
    // window becomes visible/foreground.
    document.addEventListener("visibilitychange", function () {
      if (!document.hidden) loadTranscripts();
    });
    window.addEventListener("focus", function () { loadTranscripts(); });
    // CR (2026-06-23) — live refresh on new dictation. Bank pushes
    // `transcript-added` from app.py right after store.append_transcript,
    // so the list updates without having to switch tabs.
    if (window.lumo && window.lumo.on) {
      window.lumo.on("transcript-added", function () { loadTranscripts(); });
    }
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();
