// shiwake LP — Tweaks panel for live bold-highlighting copy
// Enabled via ?tweaks=1 in URL. Persists list in localStorage.
//
// Usage:
//   1. Open  https://shiwake-lp.pages.dev/?tweaks=1
//   2. Select text on the page → click "選択中を Bold 化"
//   3. Manage list in the side panel, see live preview
//   4. "コード出力" で JSON をクリップボードへ。コードに焼き付ける際に使う
//
// Skips text inside .phone-shell, .pain-pulse-accent, script/style, and the panel itself.

const LS_KEY = "lp-tweaks-bold-v1";

function loadList() {
  try { return JSON.parse(localStorage.getItem(LS_KEY) || "[]"); } catch { return []; }
}
function saveList(items) {
  try { localStorage.setItem(LS_KEY, JSON.stringify(items)); } catch {}
}

/* DOM walking + bolding ---------------------------------------------------- */

function clearTweaks() {
  document.querySelectorAll('[data-tweaked-bold="1"]').forEach((el) => {
    const txt = document.createTextNode(el.textContent || "");
    el.parentNode?.replaceChild(txt, el);
  });
  // normalize: merge adjacent text nodes
  document.body.normalize();
}

function escapeRegex(s) {
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function applyBolding(items) {
  clearTweaks();
  if (!items || !items.length) return;
  // Sort by length desc so longer items win over shorter substring overlap
  const sorted = items.slice().sort((a, b) => b.length - a.length);
  const pattern = sorted.map(escapeRegex).join("|");
  const regex = new RegExp(pattern, "g");

  const skipIds = new Set(["lp-tweaks-panel", "lp-tweaks-fab"]);
  const skipClasses = ["phone-shell", "pain-pulse-accent", "mono", "mono-sm"];

  const walker = document.createTreeWalker(
    document.body,
    NodeFilter.SHOW_TEXT,
    {
      acceptNode(node) {
        if (!node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
        let p = node.parentNode;
        while (p && p !== document.body) {
          if (p.nodeType === 1) {
            if (skipIds.has(p.id)) return NodeFilter.FILTER_REJECT;
            if (p.tagName === "SCRIPT" || p.tagName === "STYLE") return NodeFilter.FILTER_REJECT;
            if (p.tagName === "INPUT" || p.tagName === "TEXTAREA") return NodeFilter.FILTER_REJECT;
            if (p.tagName === "STRONG" && p.getAttribute("data-tweaked-bold") === "1") return NodeFilter.FILTER_REJECT;
            if (p.classList) {
              for (const c of skipClasses) {
                if (p.classList.contains(c)) return NodeFilter.FILTER_REJECT;
              }
            }
          }
          p = p.parentNode;
        }
        return NodeFilter.FILTER_ACCEPT;
      },
    }
  );

  const texts = [];
  let n;
  while ((n = walker.nextNode())) texts.push(n);

  for (const textNode of texts) {
    const txt = textNode.nodeValue || "";
    regex.lastIndex = 0;
    if (!regex.test(txt)) continue;
    regex.lastIndex = 0;

    const frag = document.createDocumentFragment();
    let lastIdx = 0;
    let m;
    while ((m = regex.exec(txt)) !== null) {
      if (m.index > lastIdx) frag.appendChild(document.createTextNode(txt.slice(lastIdx, m.index)));
      const strong = document.createElement("strong");
      strong.setAttribute("data-tweaked-bold", "1");
      strong.style.fontWeight = "700";
      strong.style.background = "linear-gradient(transparent 55%, rgba(196,106,78,0.22) 55%)";
      strong.textContent = m[0];
      frag.appendChild(strong);
      lastIdx = m.index + m[0].length;
    }
    if (lastIdx < txt.length) frag.appendChild(document.createTextNode(txt.slice(lastIdx)));

    textNode.parentNode?.replaceChild(frag, textNode);
  }
}

/* TweaksPanel React component -------------------------------------------- */

function TweaksPanel() {
  const [items, setItems] = React.useState(loadList);
  const [open, setOpen] = React.useState(true);
  const [input, setInput] = React.useState("");
  const [selectionPreview, setSelectionPreview] = React.useState("");

  // Apply + persist on every change
  React.useEffect(() => {
    saveList(items);
    // delay so React's render settles before DOM manipulation
    const t = setTimeout(() => applyBolding(items), 50);
    return () => clearTimeout(t);
  }, [items]);

  // Track current selection for the "選択中をBold化" button
  React.useEffect(() => {
    const onSel = () => {
      const sel = window.getSelection()?.toString().trim() || "";
      setSelectionPreview(sel);
    };
    document.addEventListener("selectionchange", onSel);
    return () => document.removeEventListener("selectionchange", onSel);
  }, []);

  const addItem = (txt) => {
    const t = (txt || "").trim();
    if (!t) return;
    if (items.includes(t)) return;
    setItems([...items, t]);
  };
  const addFromSelection = () => {
    if (selectionPreview) {
      addItem(selectionPreview);
      window.getSelection()?.removeAllRanges();
    }
  };
  const addFromInput = () => {
    addItem(input);
    setInput("");
  };
  const removeAt = (idx) => setItems(items.filter((_, i) => i !== idx));
  const clearAll = () => {
    if (confirm("Bold 化リストを全削除しますか？")) setItems([]);
  };
  const copyJson = () => {
    const json = JSON.stringify(items, null, 2);
    navigator.clipboard?.writeText(json).then(
      () => alert("クリップボードにコピーしました\n\n" + json),
      () => prompt("コピーできなかったので手動でコピーしてください", json),
    );
  };

  /* FAB (collapsed) */
  if (!open) {
    return (
      <button
        id="lp-tweaks-fab"
        onClick={() => setOpen(true)}
        style={{
          position: "fixed", right: 20, bottom: 20, zIndex: 9999,
          padding: "10px 16px", borderRadius: 99,
          background: "var(--ink)", color: "var(--bg)",
          border: 0, cursor: "pointer",
          fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.14em",
          boxShadow: "0 8px 24px -8px rgba(0,0,0,0.4)",
        }}
      >✎ TWEAKS ({items.length})</button>
    );
  }

  /* Panel */
  return (
    <div
      id="lp-tweaks-panel"
      style={{
        position: "fixed", right: 16, top: 16, bottom: 16, width: 360, zIndex: 9999,
        background: "#fff",
        border: "1px solid var(--line)",
        borderRadius: 12,
        boxShadow: "0 16px 48px -12px rgba(0,0,0,0.25)",
        display: "flex", flexDirection: "column",
        fontFamily: "var(--sans)",
        color: "var(--ink)",
      }}
    >
      {/* Header */}
      <div style={{
        padding: "14px 16px", borderBottom: "1px solid var(--line-2)",
        display: "flex", alignItems: "center", justifyContent: "space-between",
      }}>
        <div>
          <div className="mono-sm" style={{ marginBottom: 2 }}>TWEAKS</div>
          <div style={{ fontSize: 14, fontWeight: 600 }}>Bold 強調エディタ</div>
        </div>
        <button
          onClick={() => setOpen(false)}
          style={{
            background: "transparent", border: 0, cursor: "pointer",
            fontSize: 18, color: "var(--ink-2)", padding: 4,
          }}
          title="閉じる(リストは保持されます)"
        >×</button>
      </div>

      {/* Add controls */}
      <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line-2)" }}>
        <button
          onClick={addFromSelection}
          disabled={!selectionPreview}
          style={{
            width: "100%", padding: "10px 0",
            background: selectionPreview ? "var(--accent)" : "var(--bg-2)",
            color: selectionPreview ? "#fff" : "var(--ink-3)",
            border: 0, borderRadius: 6, cursor: selectionPreview ? "pointer" : "not-allowed",
            fontSize: 12, fontWeight: 600, marginBottom: 8,
          }}
        >
          {selectionPreview
            ? `＋ 選択中「${selectionPreview.length > 18 ? selectionPreview.slice(0, 18) + "…" : selectionPreview}」を Bold 化`
            : "（テキストを選択すると追加できます）"}
        </button>
        <div style={{ display: "flex", gap: 6 }}>
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            onKeyDown={(e) => { if (e.key === "Enter") addFromInput(); }}
            placeholder="または、文字列を入力して追加"
            style={{
              flex: 1, padding: "8px 10px",
              border: "1px solid var(--line)", borderRadius: 6,
              fontSize: 12,
            }}
          />
          <button
            onClick={addFromInput}
            disabled={!input.trim()}
            style={{
              padding: "8px 14px",
              background: input.trim() ? "var(--ink)" : "var(--bg-2)",
              color: input.trim() ? "#fff" : "var(--ink-3)",
              border: 0, borderRadius: 6, cursor: input.trim() ? "pointer" : "not-allowed",
              fontSize: 12, fontWeight: 600,
            }}
          >追加</button>
        </div>
      </div>

      {/* List */}
      <div style={{ flex: 1, overflowY: "auto", padding: "8px 12px" }}>
        {items.length === 0 ? (
          <div style={{ padding: "32px 16px", textAlign: "center", color: "var(--ink-3)", fontSize: 12 }}>
            まだ何も登録されていません。<br/>
            ページ上のテキストを選択するか、上の入力欄から追加してください。
          </div>
        ) : (
          items.map((s, i) => (
            <div key={i} style={{
              display: "flex", alignItems: "center", justifyContent: "space-between",
              padding: "8px 10px", marginBottom: 4,
              background: "var(--bg-2)", borderRadius: 6,
              fontSize: 12,
            }}>
              <span style={{ flex: 1, marginRight: 8, wordBreak: "break-all" }}>
                <strong style={{
                  fontWeight: 700,
                  background: "linear-gradient(transparent 55%, rgba(196,106,78,0.22) 55%)",
                }}>{s}</strong>
              </span>
              <button
                onClick={() => removeAt(i)}
                style={{
                  background: "transparent", border: 0, cursor: "pointer",
                  fontSize: 14, color: "var(--ink-3)", padding: 2,
                  flexShrink: 0,
                }}
                title="削除"
              >×</button>
            </div>
          ))
        )}
      </div>

      {/* Footer */}
      <div style={{
        padding: "10px 16px", borderTop: "1px solid var(--line-2)",
        display: "flex", gap: 8,
      }}>
        <button
          onClick={clearAll}
          disabled={items.length === 0}
          style={{
            flex: 1, padding: "8px 0",
            background: "transparent", color: "var(--ink-3)",
            border: "1px solid var(--line)", borderRadius: 6,
            cursor: items.length ? "pointer" : "not-allowed",
            fontSize: 11,
          }}
        >全削除</button>
        <button
          onClick={copyJson}
          disabled={items.length === 0}
          style={{
            flex: 2, padding: "8px 0",
            background: items.length ? "var(--accent)" : "var(--bg-2)",
            color: items.length ? "#fff" : "var(--ink-3)",
            border: 0, borderRadius: 6,
            cursor: items.length ? "pointer" : "not-allowed",
            fontSize: 11, fontWeight: 600,
          }}
        >📋 JSON コピー</button>
      </div>
    </div>
  );
}

/* Expose globally so app.jsx can use */
Object.assign(window, { TweaksPanel, applyBolding, loadTweaksList: loadList });
