// Prism Translate — image / manga OCR + translation. Local Tesseract, then AI translation.
import { LANGS } from "../lib/storage.js";
import { mountBrand } from "../lib/brand.js";

const $ = (s) => document.querySelector(s);
const statusEl = $("#status");
const dropEl = $("#drop");
const stageEl = $("#stage");
const imgEl = $("#img");
const overlayEl = $("#overlay");
const listEl = $("#list");

let cfg = { targetLang: "zh-CN", sourceLang: "auto", scenario: "general" };
const status = (t) => (statusEl.textContent = t);

function sendBg(msg) {
  return new Promise((res) => chrome.runtime.sendMessage(msg, (r) => {
    if (chrome.runtime.lastError) res({ ok: false, error: chrome.runtime.lastError.message });
    else res(r || { ok: false });
  }));
}

async function init() {
  mountBrand();
  const r = await sendBg({ cmd: "getState" });
  if (r.ok) {
    cfg = { targetLang: r.settings.targetLang, sourceLang: r.settings.sourceLang, scenario: r.settings.scenario };
    const t = $("#target");
    LANGS.filter((l) => l.code !== "auto").forEach((l) => {
      const o = document.createElement("option");
      o.value = l.code; o.textContent = l.name; if (l.code === cfg.targetLang) o.selected = true;
      t.appendChild(o);
    });
    const sc = $("#scenario");
    r.prompts.forEach((p) => {
      const o = document.createElement("option");
      o.value = p.id; o.textContent = p.name; if (p.id === cfg.scenario) o.selected = true;
      sc.appendChild(o);
    });
    t.addEventListener("change", (e) => (cfg.targetLang = e.target.value));
    sc.addEventListener("change", (e) => (cfg.scenario = e.target.value));
    // default OCR lang from source
    if (cfg.sourceLang === "ja") $("#ocrlang").value = "jpn+eng";
    else if (cfg.sourceLang && cfg.sourceLang.startsWith("zh")) $("#ocrlang").value = "chi_sim+eng";
  }

  $("#open").addEventListener("click", () => $("#file").click());
  $("#file").addEventListener("change", (e) => { if (e.target.files[0]) loadBlob(e.target.files[0]); });
  dropEl.addEventListener("dragover", (e) => { e.preventDefault(); dropEl.classList.add("over"); });
  dropEl.addEventListener("dragleave", () => dropEl.classList.remove("over"));
  dropEl.addEventListener("drop", (e) => {
    e.preventDefault(); dropEl.classList.remove("over");
    const f = e.dataTransfer.files[0];
    if (f && f.type.startsWith("image/")) loadBlob(f);
  });
  document.addEventListener("paste", (e) => {
    const item = Array.from(e.clipboardData.items).find((i) => i.type.startsWith("image/"));
    if (item) loadBlob(item.getAsFile());
  });

  const src = new URLSearchParams(location.search).get("src");
  if (src) loadUrl(src);
}

async function loadUrl(url) {
  try {
    status("下载图片…");
    const res = await fetch(url);
    if (!res.ok) { status("下载失败：HTTP " + res.status); return; }
    const blob = await res.blob();
    if (!blob.type.startsWith("image/")) { status("不是图片：" + (blob.type || "未知类型")); return; }
    loadBlob(blob);
  } catch (e) { status("下载失败：" + e.message); }
}

let curUrl = null;
function loadBlob(blob) {
  if (curUrl) URL.revokeObjectURL(curUrl); // free the previously-loaded image
  curUrl = URL.createObjectURL(blob);
  imgEl.onerror = () => status("图片解码失败");
  imgEl.onload = () => { dropEl.classList.add("hidden"); stageEl.classList.remove("hidden"); runOCR(curUrl); };
  imgEl.src = curUrl;
}

let worker = null;
async function runOCR(url) {
  overlayEl.innerHTML = "";
  listEl.innerHTML = "<h3>识别 + 译文</h3>";
  const langs = $("#ocrlang").value;
  status("加载 OCR 引擎…");
  try {
    if (!worker) {
      worker = await Tesseract.createWorker(langs, 1, {
        // MV3 worker-src 'self' forbids blob: workers, so use a same-origin worker file.
        workerBlobURL: false,
        workerPath: chrome.runtime.getURL("vendor/tesseract/worker.min.js"),
        corePath: chrome.runtime.getURL("vendor/tesseract/"),
        langPath: "https://tessdata.projectnaptha.com/4.0.0",
        logger: (m) => { if (m.status) status(`OCR：${m.status} ${Math.round((m.progress || 0) * 100)}%`); },
      });
    } else {
      await worker.reinitialize(langs);
    }
    status("识别中…");
    const { data } = await worker.recognize(url);
    const natW = imgEl.naturalWidth, natH = imgEl.naturalHeight;
    const lines = (data.lines || []).filter((l) => l.text && l.text.trim().length > 0);
    if (!lines.length) { status("未识别到文字"); return; }
    status(`识别到 ${lines.length} 行，翻译中…`);

    const texts = lines.map((l) => l.text.replace(/\s+/g, " ").trim());
    const boxes = [], rows = [];
    lines.forEach((l, i) => {
      const b = l.bbox;
      const box = document.createElement("div");
      box.className = "obox loading";
      box.style.left = (b.x0 / natW) * 100 + "%";
      box.style.top = (b.y0 / natH) * 100 + "%";
      box.style.width = ((b.x1 - b.x0) / natW) * 100 + "%";
      box.style.height = ((b.y1 - b.y0) / natH) * 100 + "%";
      box.textContent = "…";
      overlayEl.appendChild(box);
      boxes.push(box);

      const row = document.createElement("div");
      row.className = "lrow";
      row.innerHTML = `<div class="lo">${escapeHtml(texts[i])}</div><div class="lt loading">翻译中…</div>`;
      listEl.appendChild(row);
      rows.push(row);
    });

    // translate in chunks
    for (let i = 0; i < texts.length; i += 10) {
      const slice = texts.slice(i, i + 10);
      const r = await sendBg({ cmd: "translateBatch", texts: slice, targetLang: cfg.targetLang, sourceLang: cfg.sourceLang, scenario: cfg.scenario });
      slice.forEach((_, j) => {
        const idx = i + j;
        const lt = rows[idx].querySelector(".lt");
        if (r.ok) {
          const tr = r.translations[j] || "";
          lt.classList.remove("loading"); lt.textContent = tr;
          boxes[idx].classList.remove("loading"); boxes[idx].textContent = tr;
        } else {
          lt.classList.remove("loading"); lt.classList.add("err"); lt.textContent = "⚠ " + (r.error || "翻译失败");
          boxes[idx].classList.remove("loading"); boxes[idx].classList.add("err"); boxes[idx].textContent = "⚠";
        }
      });
    }
    status(`完成 · ${lines.length} 行`);
  } catch (e) {
    status("OCR 失败：" + (e.message || e));
    try { await worker?.terminate(); } finally { worker = null; } // don't reuse a broken worker
  }
}

window.addEventListener("pagehide", () => { worker?.terminate(); });

function escapeHtml(s) {
  return String(s ?? "").replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]));
}

init();
