// OCR для сканированных PDF (постранично: рендер страницы → изображение → текст).
//   - backend: POST /ocr с сырыми байтами PNG (rapidocr/Tesseract на сервере);
//   - local:   tesseract.js полностью в браузере (файлы в vendor/tesseract/, см. vendor/README.md).

import { getSettings } from "../common/storage.js";

/** Страница считается сканом, если извлечённого текста почти нет. */
export function pageLooksScanned(text) {
  return (text || "").replace(/\s+/g, "").length < 20;
}

function canvasToBlob(canvas) {
  return new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
}

// --- Backend (постранично, сырые байты изображения) -----------------------

async function ocrPageBackend(canvas, s) {
  const blob = await canvasToBlob(canvas);
  const res = await fetch(`${s.backendUrl.replace(/\/+$/, "")}/ocr`, {
    method: "POST",
    headers: {
      "Content-Type": "image/png",
      ...(s.backendToken ? { Authorization: `Bearer ${s.backendToken}` } : {}),
    },
    body: blob,
  });
  if (!res.ok) throw new Error(`OCR HTTP ${res.status}: ${await res.text()}`);
  const data = await res.json();
  if (data.error) console.warn("[Aris OCR] backend:", data.engine, data.error);
  return data.text || "";
}

// --- Локальный tesseract.js -----------------------------------------------
// Все ресурсы — локально (MV3 CSP запрещает удалённые скрипты). Ожидается в
// vendor/tesseract/: tesseract.esm.min.js, worker.min.js, tesseract-core*.wasm.js,
// и языковые данные *.traineddata(.gz). См. vendor/README.md.

let _worker = null;

/** Promise.race с таймаутом: чтобы зависший OCR-движок не блокировал загрузку. */
function withTimeout(promise, ms, label = "OCR") {
  return Promise.race([
    promise,
    new Promise((_, rej) => setTimeout(() => rej(new Error(`${label}: таймаут ${ms}мс`)), ms)),
  ]);
}

async function getLocalWorker(langs, onLog) {
  if (_worker) return _worker;
  const mod = await import(chrome.runtime.getURL("vendor/tesseract/tesseract.esm.min.js"));
  const Tesseract = mod.default || mod; // ESM-сборка экспортирует CJS-объект как default
  const { createWorker } = Tesseract;
  const u = (p) => chrome.runtime.getURL(`vendor/tesseract/${p}`);
  _worker = await withTimeout(createWorker(langs.join("+"), 1, {
    workerPath: u("worker.min.js"),
    corePath: u("tesseract-core-simd.wasm.js"),
    langPath: u("").replace(/\/$/, ""), // *.traineddata рядом, в vendor/tesseract/
    gzip: false, // данные лежат несжатыми (.traineddata)
    // MV3: создаём воркер прямо из extension-URL (не через blob), иначе
    // importScripts ресурса расширения блокируется (другой origin).
    workerBlobURL: false,
    logger: onLog ? (m) => m.status === "recognizing text" && onLog(m.progress) : undefined,
  }), 30000, "tesseract init");
  return _worker;
}

export async function ocrPageLocal(canvas, langs = ["rus", "eng"], onLog) {
  try {
    const worker = await getLocalWorker(langs, onLog);
    const { data } = await withTimeout(worker.recognize(canvas), 60000, "tesseract recognize");
    return data?.text || "";
  } catch (e) {
    console.warn("[Aris OCR] локальный движок недоступен:", e.message);
    _worker = null; // сброс, чтобы не переиспользовать зависший воркер
    return "";
  }
}

async function terminateLocal() {
  try {
    await _worker?.terminate();
  } catch {
    /* ignore */
  }
  _worker = null;
}

// --- Высокоуровневая обёртка ----------------------------------------------

/**
 * Дополняет текст страниц OCR-ом там, где это нужно (скан).
 * renderPage(i) → HTMLCanvasElement; onProgress(done, total, page) — статус.
 */
export async function ensureText({ pages, renderPage, onProgress }) {
  const s = await getSettings();
  if (!s.ocrEnabled) return pages;

  const todo = [];
  pages.forEach((t, i) => { if (pageLooksScanned(t)) todo.push(i); });
  if (!todo.length) return pages;

  const out = [...pages];
  let done = 0;
  try {
    for (const i of todo) {
      onProgress?.(done, todo.length, i + 1);
      try {
        const canvas = await renderPage(i);
        out[i] = s.ocrMode === "backend"
          ? await withTimeout(ocrPageBackend(canvas, s), 90000, "backend OCR")
          : await ocrPageLocal(canvas, s.ocrLanguages);
      } catch (e) {
        console.warn(`[Aris OCR] страница ${i + 1}:`, e.message);
      }
      done++;
    }
    onProgress?.(done, todo.length, null);
  } catch (e) {
    console.warn("[Aris OCR] прервано:", e.message);
  } finally {
    if (s.ocrMode === "local") await terminateLocal();
  }
  return out;
}
