export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const { pathname } = url;

    // CORS preflight for API
    if (request.method === "OPTIONS" && pathname.startsWith("/api/")) {
      return new Response(null, { headers: corsHeaders() });
    }

    // --- API: list languages (common) ---
    if (pathname === "/api/languages") {
      return jsonResponse({ languages: COMMON_LANGUAGES }, 200, corsHeaders());
    }

    // --- API: translate ---
    if (pathname === "/api/translate") {
      if (request.method !== "POST") {
        return jsonResponse(
          { error: "Use POST /api/translate with JSON body." },
          405,
          corsHeaders()
        );
      }

      // Optional API key
      const apiKey = env.API_KEY;
      if (apiKey) {
        const got =
          request.headers.get("x-api-key") ||
          url.searchParams.get("key") ||
          "";
        if (got !== apiKey) {
          return jsonResponse({ error: "Unauthorized" }, 401, corsHeaders());
        }
      }

      let body;
      try {
        body = await request.json();
      } catch {
        return jsonResponse({ error: "Invalid JSON body." }, 400, corsHeaders());
      }

      const text = (body.text ?? "").toString();
      const target_lang = (body.target_lang ?? "").toString().trim();
      const source_lang_raw = (body.source_lang ?? "").toString().trim(); // optional

      if (!text) return jsonResponse({ error: "text is required" }, 400, corsHeaders());
      if (!target_lang) return jsonResponse({ error: "target_lang is required" }, 400, corsHeaders());

      const page_size = clampInt(body.page_size ?? 1800, 200, 5000);
      const page = clampInt(body.page ?? 1, 1, 999999);
      const translate_all = Boolean(body.translate_all);

      const chunks = splitText(text, page_size);
      const total_pages = chunks.length;
      const safePage = clampInt(page, 1, total_pages);

      try {
        if (translate_all) {
          // Use batch requests (Workers AI supports batch schema on this model) :contentReference[oaicite:2]{index=2}
          const requests = chunks.slice(0, 50).map((t) => ({
            text: t,
            ...(source_lang_raw ? { source_lang: normalizeLang(source_lang_raw) } : {}),
            target_lang: normalizeLang(target_lang),
          }));

          const out = await env.AI.run("@cf/meta/m2m100-1.2b", { requests });
          // Some bindings return array-like results; normalize defensively:
          const translated_pages = Array.isArray(out)
            ? out.map((x) => x?.translated_text ?? "")
            : (out?.responses ?? out?.results ?? out?.translated_pages ?? null);

          // Fallback: if unknown, just translate one-by-one
          if (Array.isArray(translated_pages)) {
            return jsonResponse(
              {
                page: 1,
                page_size,
                total_pages,
                translated_pages,
                translated_text: translated_pages.join(""),
                truncated: chunks.length > 50,
              },
              200,
              corsHeaders()
            );
          }
        }

        // Default: translate one page
        const chunk = chunks[safePage - 1] ?? "";
        const out = await env.AI.run("@cf/meta/m2m100-1.2b", {
          text: chunk,
          ...(source_lang_raw ? { source_lang: normalizeLang(source_lang_raw) } : {}),
          target_lang: normalizeLang(target_lang),
        });

        return jsonResponse(
          {
            page: safePage,
            page_size,
            total_pages,
            source_lang: source_lang_raw || "(default en)",
            target_lang,
            input_text: chunk,
            translated_text: out?.translated_text ?? "",
          },
          200,
          corsHeaders()
        );
      } catch (e) {
        return jsonResponse(
          { error: "AI run failed", detail: String(e?.message || e) },
          500,
          corsHeaders()
        );
      }
    }

    // --- Web UI ---
    if (pathname === "/" || pathname === "/index.html") {
      if (request.method === "GET") {
        return new Response(renderPage({}), {
          headers: { "content-type": "text/html; charset=utf-8" },
        });
      }

      if (request.method === "POST") {
        const form = await request.formData();
        const text = (form.get("text") ?? "").toString();
        const source_lang = (form.get("source_lang") ?? "en").toString().trim();
        const target_lang = (form.get("target_lang") ?? "zh").toString().trim();
        const page_size = clampInt(form.get("page_size") ?? 1800, 200, 5000);
        const page = clampInt(form.get("page") ?? 1, 1, 999999);

        if (!text) {
          return new Response(renderPage({ error: "请输入要翻译的文本。", text, source_lang, target_lang, page_size, page }), {
            headers: { "content-type": "text/html; charset=utf-8" },
          });
        }

        const chunks = splitText(text, page_size);
        const total_pages = chunks.length;
        const safePage = clampInt(page, 1, total_pages);
        const chunk = chunks[safePage - 1] ?? "";

        let translated = "";
        let err = "";
        try {
          const out = await env.AI.run("@cf/meta/m2m100-1.2b", {
            text: chunk,
            source_lang: normalizeLang(source_lang),
            target_lang: normalizeLang(target_lang),
          });
          translated = out?.translated_text ?? "";
        } catch (e) {
          err = `翻译失败：${String(e?.message || e)}`;
        }

        return new Response(
          renderPage({
            text,
            source_lang,
            target_lang,
            page_size,
            page: safePage,
            total_pages,
            input_chunk: chunk,
            translated_text: translated,
            error: err,
          }),
          { headers: { "content-type": "text/html; charset=utf-8" } }
        );
      }

      return new Response("Method Not Allowed", { status: 405 });
    }

    return new Response("Not Found", { status: 404 });
  },
};

// ---------------- helpers ----------------

const COMMON_LANGUAGES = [
  { code: "en", name: "English" },
  { code: "zh", name: "中文" },
  { code: "ja", name: "日本語" },
  { code: "ko", name: "한국어" },
  { code: "fr", name: "Français" },
  { code: "de", name: "Deutsch" },
  { code: "es", name: "Español" },
  { code: "pt", name: "Português" },
  { code: "it", name: "Italiano" },
  { code: "ru", name: "Русский" },
  { code: "ar", name: "العربية" },
  { code: "hi", name: "हिन्दी" },
  { code: "id", name: "Bahasa Indonesia" },
  { code: "th", name: "ไทย" },
  { code: "vi", name: "Tiếng Việt" },
  { code: "tr", name: "Türkçe" },
  { code: "nl", name: "Nederlands" },
  { code: "sv", name: "Svenska" },
  { code: "pl", name: "Polski" },
  { code: "uk", name: "Українська" },
];

function normalizeLang(lang) {
  // Docs say language code like 'en'/'es' etc. :contentReference[oaicite:3]{index=3}
  // But examples sometimes use english/french; we accept both by passing through.
  return String(lang || "").trim().toLowerCase();
}

function clampInt(v, min, max) {
  const n = Number.parseInt(String(v), 10);
  if (!Number.isFinite(n)) return min;
  return Math.min(max, Math.max(min, n));
}

function splitText(text, maxChars) {
  const t = String(text || "");
  if (t.length <= maxChars) return [t];

  // Prefer paragraph-based splitting
  const parts = t.split(/\n{2,}/);
  const chunks = [];
  let buf = "";

  for (const p of parts) {
    const piece = (p + "\n\n");
    if (piece.length > maxChars) {
      // Hard split long paragraph
      if (buf) {
        chunks.push(buf);
        buf = "";
      }
      for (let i = 0; i < piece.length; i += maxChars) {
        chunks.push(piece.slice(i, i + maxChars));
      }
      continue;
    }

    if (buf.length + piece.length > maxChars) {
      chunks.push(buf);
      buf = piece;
    } else {
      buf += piece;
    }
  }
  if (buf) chunks.push(buf);
  return chunks;
}

function escapeHtml(s) {
  return String(s || "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

function renderPage(state) {
  const {
    text = "",
    source_lang = "en",
    target_lang = "zh",
    page_size = 1800,
    page = 1,
    total_pages = 1,
    input_chunk = "",
    translated_text = "",
    error = "",
  } = state || {};

  const langOptions = (selected) =>
    COMMON_LANGUAGES.map(
      (l) =>
        `<option value="${escapeHtml(l.code)}" ${
          l.code === selected ? "selected" : ""
        }>${escapeHtml(l.name)} (${escapeHtml(l.code)})</option>`
    ).join("");

  const hasResult = Boolean(text);

  return `<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Workers AI 翻译</title>
  <style>
    body { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; margin: 24px; }
    .wrap { max-width: 1100px; margin: 0 auto; }
    textarea { width: 100%; min-height: 220px; padding: 12px; font-size: 14px; }
    pre { white-space: pre-wrap; word-break: break-word; background: #f6f7f9; padding: 12px; border-radius: 10px; }
    .row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
    .card { border: 1px solid #e6e7ea; border-radius: 14px; padding: 14px; margin-top: 14px; }
    .btn { padding: 10px 14px; border-radius: 10px; border: 1px solid #d0d3d9; background: #fff; cursor: pointer; }
    .btn-primary { border-color: #111; }
    .muted { color: #666; font-size: 12px; }
    .err { color: #b00020; }
    input[type="number"], input[type="text"], select { padding: 8px 10px; border-radius: 10px; border: 1px solid #d0d3d9; }
  </style>
</head>
<body>
  <div class="wrap">
    <h2>Workers AI 翻译（m2m100-1.2b）</h2>
    <div class="muted">
      默认网页访问：英译中（en → zh）。语言参数遵循语言代码（如 en/zh/es…） :contentReference[oaicite:4]{index=4}
    </div>

    ${error ? `<p class="err">${escapeHtml(error)}</p>` : ""}

    <form method="POST" action="/">
      <div class="card">
        <div class="row">
          <label>源语言：</label>
          <select name="source_lang">
            ${langOptions(source_lang)}
          </select>

          <label>目标语言：</label>
          <select name="target_lang">
            ${langOptions(target_lang)}
          </select>

          <label>每页字符数：</label>
          <input name="page_size" type="number" min="200" max="5000" value="${escapeHtml(page_size)}" />

          <input type="hidden" name="page" value="1" />
          <button class="btn btn-primary" type="submit">翻译（从第 1 页开始）</button>
        </div>

        <div style="margin-top: 10px;">
          <textarea name="text" placeholder="粘贴要翻译的文本…">${escapeHtml(text)}</textarea>
        </div>

        <div class="muted">提示：长文本会自动分页；翻页时只翻译当前页，响应更快。</div>
      </div>
    </form>

    ${
      hasResult
        ? `<div class="card">
            <div class="row" style="justify-content: space-between;">
              <div><b>第 ${page} / ${total_pages} 页</b></div>
              <div class="row">
                ${page > 1 ? navForm("上一页", page - 1, text, source_lang, target_lang, page_size) : ""}
                ${page < total_pages ? navForm("下一页", page + 1, text, source_lang, target_lang, page_size) : ""}
              </div>
            </div>

            <h3>原文（本页）</h3>
            <pre>${escapeHtml(input_chunk || "")}</pre>

            <h3>译文</h3>
            <pre>${escapeHtml(translated_text || "")}</pre>

            <div class="muted">
              API：POST /api/translate（JSON）｜GET /api/languages
            </div>
          </div>`
        : ""
    }
  </div>
</body>
</html>`;
}

function navForm(label, page, text, source_lang, target_lang, page_size) {
  return `<form method="POST" action="/" style="display:inline;">
    <input type="hidden" name="page" value="${escapeHtml(page)}" />
    <input type="hidden" name="source_lang" value="${escapeHtml(source_lang)}" />
    <input type="hidden" name="target_lang" value="${escapeHtml(target_lang)}" />
    <input type="hidden" name="page_size" value="${escapeHtml(page_size)}" />
    <input type="hidden" name="text" value="${escapeHtml(text)}" />
    <button class="btn" type="submit">${escapeHtml(label)}</button>
  </form>`;
}

function corsHeaders() {
  return {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type, X-API-Key",
    "Access-Control-Max-Age": "86400",
  };
}

function jsonResponse(obj, status = 200, extraHeaders = {}) {
  return new Response(JSON.stringify(obj, null, 2), {
    status,
    headers: {
      "content-type": "application/json; charset=utf-8",
      ...extraHeaders,
    },
  });
}
