// Edit lyrics in the extension, like the app: fix wording, add/remove/reorder lines.
// Spanish and English stay PAIRED per row (the interleave + timing + original-line-index
// mapping all depend on es/en being line-parallel). Saved to chrome.storage.local under
// `eleje_lyric_edits` keyed by song; the panel then prefers the edit for display AND
// timing, and it exports for folding back into the app's lyrics.json (merge_lyrics.py).
// Self-contained (own `esc`, no imports) so the content script can dynamic-import it
// into the YouTube overlay's shadow DOM as well as the side panel.
function esc(s) {
  return (s == null ? "" : String(s)).replace(/&/g, "&amp;").replace(/</g, "&lt;")
    .replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}

// Append extra recording credits to a "Grabado por …" line, in natural Spanish.
// PURE + exported (unit-tested). `entries` = [{o, s}] where o = orquesta, s = voz/cantor(es).
// Mirrors the real corpus phrasing: "con la voz de X" (one voice) / "con las voces de X y Y"
// (two) / "…, X, Y y Z" (three+, Oxford "y"); orchestras and voices joined with " y ".
export function formatGrabaciones(baseBy, entries) {
  const frags = [];
  for (const e of (entries || [])) {
    const o = String((e && e.o) || "").trim();
    const voces = String((e && e.s) || "")
      .split(/\s*(?:,|;|\/|&| y | and )\s*/i).map(v => v.trim()).filter(Boolean);
    if (!o && !voces.length) continue;                    // skip empty rows
    let frag;
    if (o && voces.length) frag = "la orquesta de " + o + " " + vozClause(voces, false);
    else if (o)            frag = "la orquesta de " + o;
    else                   frag = vozClause(voces, true); // singer-only
    frags.push(frag);
  }
  if (!frags.length) return baseBy || "";                 // nothing to add → unchanged base
  const joined = joinY(frags);
  const base = String(baseBy || "").trim().replace(/[.;\s]+$/, "");  // strip trailing . ; ws
  if (!base) return "Grabado por " + joined + ".";        // empty base → start the line
  return base + " y " + joined + ".";                     // natural append + final period
}
function vozClause(voces, standalone) {
  const names = joinY(voces);
  if (standalone) return voces.length > 1 ? "las voces de " + names : "la voz de " + names;
  return voces.length > 1 ? "con las voces de " + names : "con la voz de " + names;
}
function joinY(arr) {
  if (arr.length <= 1) return arr[0] || "";
  if (arr.length === 2) return arr[0] + " y " + arr[1];
  return arr.slice(0, -1).join(", ") + " y " + arr[arr.length - 1];   // Oxford "y" for 3+
}

const STORE_KEY = "eleje_lyric_edits";

function store() {
  return (typeof chrome !== "undefined" && chrome.storage && chrome.storage.local) || null;
}
async function readAll() {
  const s = store();
  if (!s) return {};
  const o = await s.get(STORE_KEY);
  return (o && o[STORE_KEY]) || {};
}
export async function loadLyricEdit(songId) {
  if (!songId) return null;
  return (await readAll())["song:" + songId] || null;
}
export async function allLyricEdits() {
  return Object.values(await readAll()).sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
}
export async function saveLyricEdit(e) {
  const s = store();
  if (!s || !e || !e.songId) return false;
  const all = await readAll();
  all["song:" + e.songId] = e;
  await s.set({ [STORE_KEY]: all });
  return true;
}
export async function deleteLyricEdit(songId) {
  const s = store();
  if (!s || !songId) return false;
  const all = await readAll();
  if (!(("song:" + songId) in all)) return false;
  delete all["song:" + songId];
  await s.set({ [STORE_KEY]: all });
  return true;
}
export async function clearLyricEdits() {
  const s = store();
  if (s) await s.set({ [STORE_KEY]: {} });
}

function nowMs() { try { return Date.now(); } catch { return 0; } }

// Build paired {es,en} rows from a lyrics record (pad the shorter side).
function rowsFrom(ly) {
  const es = (ly && ly.es ? ly.es : "").split("\n");
  const en = (ly && ly.en ? ly.en : "").split("\n");
  const n = Math.max(es.length, en.length, 1);
  const rows = [];
  for (let i = 0; i < n; i++) rows.push({ es: es[i] != null ? es[i] : "", en: en[i] != null ? en[i] : "" });
  return rows;
}

// Mount the editor into `slot`. `ly` is the current (possibly already-edited) lyrics or
// null (start blank to ADD lyrics). `onDone(saved)` is called on Cancel/after Save-close.
export function mountLyricsEditor({ slot, song, ly, onDone }) {
  let rows = rowsFrom(ly);
  // Credits are ADD-ONLY. `byBase` = the existing canonical "Grabado por …" line — READ-ONLY,
  // the user can't rewrite it. `grabaciones` = [{o,s}] the user appends. `by` = the recomputed
  // credits shown read-only. Additions are recorded for owner review on the backend (R2) via the
  // normal contribution path; removing all additions restores `byBase` exactly.
  let grabaciones = (ly && Array.isArray(ly.grabaciones)) ? ly.grabaciones.slice() : [];
  let byBase = (ly && ly.byBase != null) ? ly.byBase : ((ly && ly.by) || "");
  let by = formatGrabaciones(byBase, grabaciones);

  const rowHTML = (r, i) => `<div class="le-row" data-i="${i}">
      <textarea class="le-es" rows="1" placeholder="Español…">${esc(r.es)}</textarea>
      <textarea class="le-en" rows="1" placeholder="English…">${esc(r.en)}</textarea>
      <div class="le-ctl">
        <button class="le-up" title="Move up" ${i === 0 ? "disabled" : ""}>↑</button>
        <button class="le-del" title="Remove line">×</button>
      </div>
    </div>`;

  const syncFromDOM = () => {
    // Only the lyric rows are user-editable now; credits (byBase) are read-only, `by` derives
    // from formatGrabaciones(byBase, grabaciones) and is updated by the add/remove handlers.
    rows = [...slot.querySelectorAll(".le-row")].map(el => ({
      es: el.querySelector(".le-es").value,
      en: el.querySelector(".le-en").value,
    }));
  };

  const render = (focusRow) => {
    slot.innerHTML = `<div class="section-h">Edit lyrics &amp; credits — ${esc(song.t)}
        <button class="time-btn" id="le-cancel">Cancel</button></div>
      <div class="le-tip">Edit any line; <b>+ line</b> adds, <b>×</b> removes, <b>↑</b> reorders.
        Spanish &amp; English stay paired row-by-row. The <b>Grabado / credits</b> can't be rewritten —
        use <b>+ Añadir grabación</b> to add another orchestra/voice (saved for review). English optional.</div>
      <div class="le-rows" id="le-rows">${rows.map(rowHTML).join("")}</div>
      <label style="display:block;font:600 11px/1.4 inherit;opacity:.72;margin:10px 0 3px">Grabado / credits</label>
      <div class="le-by-view" id="le-by-view">${by ? esc(by)
        : `<span class="le-by-empty">Sin créditos todavía — añade una grabación.</span>`}</div>
      <div class="le-grab" id="le-grab">
        <div class="le-grab-add">
          <input class="le-grab-o" id="le-grab-o" type="text" placeholder="Orquesta — p. ej. Aníbal Troilo">
          <input class="le-grab-s" id="le-grab-s" type="text" placeholder="Voz / cantor(es) — p. ej. Alberto Morán">
          <button class="pt-mini" id="le-grab-btn" type="button" title="Añade otra grabación a los créditos (se guarda para revisión)">+ Añadir grabación</button>
        </div>
        ${grabaciones.length ? `<div class="le-grab-h">Tus grabaciones añadidas · pendiente de revisión</div>
        <ul class="le-grab-list" id="le-grab-list">${grabaciones.map((g, i) =>
          `<li class="le-grab-item" data-gi="${i}"><span>${esc(g.o)}${g.o && g.s ? " · " : ""}${esc(g.s)}</span>
             <button class="le-grab-del" type="button" title="Quitar / Remove">×</button></li>`).join("")}</ul>` : ""}
      </div>
      <div class="le-foot">
        <button class="pt-mini" id="le-add">+ line</button>
        <button class="btn" id="le-save">✓ Save lyrics</button>
        <button class="pt-mini" id="le-export" title="Download all your lyric edits as JSON">Export edits</button>
        <span class="pt-saved" id="le-saved" hidden></span>
      </div>`;
    wire();
    const rowsEl = slot.querySelectorAll(".le-row");
    const target = focusRow != null && rowsEl[focusRow] ? rowsEl[focusRow] : null;
    if (target) target.querySelector(".le-es").focus();
  };

  const grow = (t) => { t.style.height = "auto"; t.style.height = t.scrollHeight + "px"; };

  const wire = () => {
    slot.querySelector("#le-cancel").addEventListener("click", () => onDone && onDone(false));
    slot.querySelector("#le-add").addEventListener("click", () => { syncFromDOM(); rows.push({ es: "", en: "" }); render(rows.length - 1); });
    // Add a grabación: append a structured {orquesta, voz} credit to the read-only "Grabado por …"
    // line (the base credits can't be rewritten — only appended to). Recorded for review on save.
    slot.querySelector("#le-grab-btn").addEventListener("click", () => {
      syncFromDOM();   // keep any lyric-row edits before the re-render
      const o = slot.querySelector("#le-grab-o").value.trim();
      const s = slot.querySelector("#le-grab-s").value.trim();
      if (!o && !s) return;
      grabaciones.push({ o, s });
      by = formatGrabaciones(byBase, grabaciones);   // base is read-only; only additions grow `by`
      render();
      slot.querySelector("#le-grab-o").focus();       // ready for the next entry
    });
    slot.querySelectorAll(".le-grab-del").forEach(btn => {
      btn.addEventListener("click", () => {
        syncFromDOM();
        const li = btn.closest(".le-grab-item");
        grabaciones.splice(+li.dataset.gi, 1);
        by = formatGrabaciones(byBase, grabaciones);   // removing all restores the base credits
        render();
      });
    });
    slot.querySelector("#le-save").addEventListener("click", async () => {
      syncFromDOM();
      // drop fully-empty trailing rows
      while (rows.length && !rows[rows.length - 1].es.trim() && !rows[rows.length - 1].en.trim()) rows.pop();
      const edit = {
        songId: song.id,
        song: { t: song.t, o: song.o, s: song.s, y: song.y },
        es: rows.map(r => r.es).join("\n"),
        en: rows.map(r => r.en).join("\n"),
        by: by,                 // FINAL grown "Grabado por …" text (display + merge read this)
        byBase: byBase,         // credits before structured entries (editor-only, for re-edit)
        grabaciones: grabaciones,   // [{o,s}] in append order (editor-only + backend signal)
        updatedAt: nowMs(),
      };
      const ok = await saveLyricEdit(edit);
      const sv = slot.querySelector("#le-saved");
      sv.hidden = false;
      sv.textContent = ok
        ? `Saved ✓ ${rows.length} lines${grabaciones.length ? ` · ${grabaciones.length} grabación${grabaciones.length > 1 ? "es" : ""} para revisión` : ""}`
        : "Couldn’t save (storage unavailable).";
    });
    slot.querySelector("#le-export").addEventListener("click", async () => {
      const all = await allLyricEdits();
      if (!all.length) { const sv = slot.querySelector("#le-saved"); sv.hidden = false; sv.textContent = "No saved edits yet."; return; }
      const blob = new Blob([JSON.stringify(all, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url; a.download = "eleje-lyric-edits-" + new Date().toISOString().slice(0, 10) + ".json";
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 2000);
    });
    slot.querySelectorAll(".le-row").forEach(el => {
      el.querySelector(".le-del").addEventListener("click", () => { syncFromDOM(); rows.splice(+el.dataset.i, 1); if (!rows.length) rows.push({ es: "", en: "" }); render(); });
      el.querySelector(".le-up").addEventListener("click", () => {
        const i = +el.dataset.i;
        if (i > 0) { syncFromDOM(); const t = rows[i - 1]; rows[i - 1] = rows[i]; rows[i] = t; render(i - 1); }
      });
    });
    slot.querySelectorAll("textarea").forEach(t => { t.addEventListener("input", () => grow(t)); grow(t); });
    // In the YouTube overlay (shadow DOM) keystrokes otherwise reach YouTube's shortcuts — Space
    // would pause the video instead of typing. Stop them at the field (harmless in the side panel).
    slot.querySelectorAll("input, textarea").forEach(el =>
      el.addEventListener("keydown", (e) => e.stopPropagation()));
  };

  render();
  return { teardown() {} };
}
