// Prism Translate — background service worker (MV3, module).
// Central orchestrator: translation requests, model comparison, provider tests,
// in-memory cache, usage accounting, context menus, keyboard command.

import {
  getState,
  saveSettings,
  saveProviders,
  savePrompts,
  saveGlossary,
  resetUsage,
  addUsage,
  langName,
  hashStr,
  defaultProviders,
  defaultPrompts,
  DEFAULT_SETTINGS,
} from "../lib/storage.js";
import { translateBatch, translateText, callLLM } from "../lib/providers.js";
import { buildSystemPrompt } from "../lib/prompts.js";

// ---- Session translation cache: key -> translated text ----
const cache = new Map();
const MAX_CACHE = 5000;
function cacheKey(text, providerId, target, scenario) {
  return `${providerId}|${target}|${scenario}|${hashStr(text)}`;
}
function cacheGet(k, enabled) {
  return enabled ? cache.get(k) : undefined;
}
function cacheSet(k, v, enabled) {
  if (!enabled) return;
  if (cache.size > MAX_CACHE) cache.clear();
  cache.set(k, v);
}

async function resolveProvider(state, providerId) {
  const { providers, settings } = state;
  let p = providers.find((x) => x.id === providerId);
  if (!p) p = providers.find((x) => x.id === settings.defaultProvider);
  if (!p) p = providers.find((x) => x.enabled);
  if (!p) p = providers[0];
  return p;
}

function resolveSystem(state, scenarioId, targetLang, sourceLang) {
  const prompt = state.prompts.find((x) => x.id === scenarioId) || state.prompts[0];
  return buildSystemPrompt(prompt, {
    targetName: langName(targetLang),
    sourceName: langName(sourceLang),
    glossary: state.glossary,
  });
}

// ---- Message router ----
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  handle(msg, sender)
    .then((r) => sendResponse({ ok: true, ...r }))
    .catch((e) => sendResponse({ ok: false, error: String(e?.message || e) }));
  return true; // keep channel open for async
});

async function handle(msg, sender) {
  switch (msg.cmd) {
    case "getState":
      return await getState();

    case "saveSettings":
      await saveSettings(msg.settings);
      return {};
    case "saveProviders":
      await saveProviders(msg.providers);
      return {};
    case "savePrompts":
      await savePrompts(msg.prompts);
      return {};
    case "saveGlossary":
      await saveGlossary(msg.glossary);
      return {};
    case "resetUsage":
      await resetUsage();
      return {};
    case "clearCache":
      cache.clear();
      return {};

    case "translateBatch":
      return await doTranslateBatch(msg);
    case "translateOne":
      return await doTranslateOne(msg);
    case "compare":
      return await doCompare(msg);
    case "testProvider":
      return await doTest(msg.provider);

    default:
      throw new Error("未知指令: " + msg.cmd);
  }
}

async function doTranslateBatch(msg) {
  const state = await getState();
  const provider = await resolveProvider(state, msg.providerId);
  if (!provider) throw new Error("未配置任何翻译服务商");
  if (!provider.apiKey && provider.type !== "deepl") {
    // deepl also needs key; just a clearer message
  }
  if (!provider.apiKey) throw new Error(`服务商「${provider.name}」未填写 API Key`);

  const target = msg.targetLang || state.settings.targetLang;
  const source = msg.sourceLang || state.settings.sourceLang;
  const scenario = msg.scenario || state.settings.scenario;
  const system = resolveSystem(state, scenario, target, source);
  const enableCache = state.settings.enableCache;

  const texts = msg.texts || [];
  const results = new Array(texts.length);
  const need = [];
  const needIdx = [];
  texts.forEach((t, i) => {
    const k = cacheKey(t, provider.id, target, scenario);
    const hit = cacheGet(k, enableCache);
    if (hit !== undefined) results[i] = hit;
    else {
      need.push(t);
      needIdx.push(i);
    }
  });

  if (need.length) {
    const r = await translateBatch(provider, need, { targetLang: target, sourceLang: source, system });
    r.translations.forEach((tr, j) => {
      const origIdx = needIdx[j];
      results[origIdx] = tr;
      cacheSet(cacheKey(texts[origIdx], provider.id, target, scenario), tr, enableCache);
    });
    await addUsage(provider.id, {
      model: r.model || provider.model,
      inputTokens: r.usage?.inputTokens || 0,
      outputTokens: r.usage?.outputTokens || 0,
      chars: r.usage?.chars || 0,
    });
  }

  return { translations: results, provider: provider.name, providerId: provider.id };
}

async function doTranslateOne(msg) {
  const r = await doTranslateBatch({ ...msg, texts: [msg.text] });
  return { text: r.translations[0], provider: r.provider, providerId: r.providerId };
}

async function doCompare(msg) {
  const state = await getState();
  const target = msg.targetLang || state.settings.targetLang;
  const source = msg.sourceLang || state.settings.sourceLang;
  const scenario = msg.scenario || state.settings.scenario;
  const system = resolveSystem(state, scenario, target, source);
  const ids = msg.providerIds && msg.providerIds.length ? msg.providerIds : state.providers.filter((p) => p.enabled).map((p) => p.id);

  const results = await Promise.all(
    ids.map(async (id) => {
      const provider = state.providers.find((p) => p.id === id);
      if (!provider) return { providerId: id, error: "服务商不存在" };
      if (!provider.apiKey) return { providerId: id, name: provider.name, model: provider.model, error: "未填写 API Key" };
      try {
        const r = await translateText(provider, msg.text, { targetLang: target, sourceLang: source, system });
        await addUsage(provider.id, {
          model: r.model || provider.model,
          inputTokens: r.usage?.inputTokens || 0,
          outputTokens: r.usage?.outputTokens || 0,
          chars: r.usage?.chars || 0,
        });
        return {
          providerId: id,
          name: provider.name,
          model: provider.model,
          text: r.text,
          ms: r.ms,
          inputTokens: r.usage?.inputTokens || 0,
          outputTokens: r.usage?.outputTokens || 0,
        };
      } catch (e) {
        return { providerId: id, name: provider.name, model: provider.model, error: String(e?.message || e) };
      }
    })
  );
  return { results };
}

async function doTest(provider) {
  if (!provider.apiKey) throw new Error("未填写 API Key");
  const sample = "Hello, world. This is a connection test.";
  const r = await translateText(provider, sample, {
    targetLang: "zh-CN",
    sourceLang: "en",
    system: "你是翻译引擎，把文本翻译为简体中文，只输出译文。",
  });
  return { ms: r.ms, sample: r.text, model: provider.model };
}

async function relayToActiveTab(payload) {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  if (!tab?.id) throw new Error("无法获取当前标签页");
  try {
    const r = await chrome.tabs.sendMessage(tab.id, payload);
    return r || {};
  } catch (e) {
    // content script may not be injected (e.g. just installed); inject then retry
    await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ["content/content.js"] }).catch(() => {});
    const r = await chrome.tabs.sendMessage(tab.id, payload).catch(() => ({}));
    return r || {};
  }
}

// ---- Context menus ----
chrome.runtime.onInstalled.addListener(async () => {
  // Seed defaults on first install.
  const cur = await chrome.storage.local.get(["settings", "providers"]);
  if (!cur.settings) await saveSettings(DEFAULT_SETTINGS);
  if (!cur.providers) await saveProviders(defaultProviders());

  chrome.contextMenus.removeAll(() => {
    chrome.contextMenus.create({ id: "prism-translate-page", title: "Prism：翻译整个页面", contexts: ["page"] });
    chrome.contextMenus.create({ id: "prism-translate-selection", title: "Prism：翻译选中文本", contexts: ["selection"] });
    chrome.contextMenus.create({ id: "prism-ocr-image", title: "Prism：翻译图片中的文字", contexts: ["image"] });
    chrome.contextMenus.create({ id: "prism-pdf-link", title: "Prism：翻译此 PDF", contexts: ["link"] });
    chrome.contextMenus.create({ id: "prism-open-workstation", title: "Prism：打开翻译工作台", contexts: ["action"] });
  });
});

chrome.contextMenus.onClicked.addListener(async (info, tab) => {
  if (info.menuItemId === "prism-translate-page" && tab?.id) {
    chrome.tabs.sendMessage(tab.id, { cmd: "translate" }).catch(() => {});
  } else if (info.menuItemId === "prism-translate-selection" && tab?.id) {
    chrome.tabs.sendMessage(tab.id, { cmd: "translateSelection", text: info.selectionText }).catch(() => {});
  } else if (info.menuItemId === "prism-ocr-image" && info.srcUrl) {
    chrome.tabs.create({ url: chrome.runtime.getURL("ocr/image-ocr.html") + "?src=" + encodeURIComponent(info.srcUrl) });
  } else if (info.menuItemId === "prism-pdf-link" && info.linkUrl) {
    chrome.tabs.create({ url: chrome.runtime.getURL("pdf/viewer.html") + "?url=" + encodeURIComponent(info.linkUrl) });
  } else if (info.menuItemId === "prism-open-workstation") {
    chrome.runtime.openOptionsPage();
  }
});

// ---- Keyboard command ----
chrome.commands.onCommand.addListener((command) => {
  if (command === "toggle-translate") relayToActiveTab({ cmd: "toggle" }).catch(() => {});
});
