import { env } from "../../core/env.js";
import { logger } from "../../core/logger.js";
import { spawn } from "node:child_process";

type TranslateResult = {
  translatedText: string;
  engine: "deepl" | "deepl_web_cli" | "google_web_v2";
  inputChars: number;
  chunkCount: number;
};

const MAX_INPUT_CHARS = 12000;
const CHUNK_SIZE = 1200;

function normalizeText(input: string): string {
  return String(input || "").trim();
}

function toErrorInfo(error: unknown): { message: string; stack?: string } {
  if (error instanceof Error) {
    return { message: error.message, stack: error.stack };
  }
  return { message: String(error) };
}

function splitText(text: string, maxLen: number): string[] {
  const out: string[] = [];
  let rest = text;
  while (rest.length > maxLen) {
    const cut = Math.max(
      rest.lastIndexOf("\n", maxLen),
      rest.lastIndexOf(". ", maxLen),
      rest.lastIndexOf("! ", maxLen),
      rest.lastIndexOf("? ", maxLen),
    );
    const idx = cut > 200 ? cut + 1 : maxLen;
    out.push(rest.slice(0, idx).trim());
    rest = rest.slice(idx).trim();
  }
  if (rest) out.push(rest);
  return out;
}

async function translateGoogleWebV2(text: string, source: string, target: string): Promise<string> {
  const endpoint = new URL("https://translate.google.com/translate_a/single");
  endpoint.searchParams.set("q", text);
  endpoint.searchParams.set("sl", source);
  endpoint.searchParams.set("tl", target);
  endpoint.searchParams.set("hl", "ko-KR");
  endpoint.searchParams.set("ie", "UTF-8");
  endpoint.searchParams.set("oe", "UTF-8");
  endpoint.searchParams.set("client", "at");
  for (const dt of ["t", "ld", "qca", "rm", "bd", "md", "ss", "ex", "sos"]) {
    endpoint.searchParams.append("dt", dt);
  }

  const response = await fetch(endpoint.toString(), {
    headers: {
      "User-Agent": "GoogleTranslate/6.27.0.08.415126308 (Linux; Android 7.1.2; Pixel 2 XL)",
      Accept: "application/json,text/plain,*/*",
    },
  });

  if (!response.ok) {
    throw new Error(`google_web_v2 실패: HTTP ${response.status}`);
  }

  const data = (await response.json()) as any;
  const parts = Array.isArray(data?.[0]) ? data[0] : [];
  const translated = parts
    .map((item: any) => String(item?.[0] || ""))
    .join("")
    .trim();

  if (!translated) {
    throw new Error("google_web_v2 응답 파싱 실패");
  }
  return translated;
}

async function translateDeepL(text: string, source: string, target: string): Promise<string> {
  if (!env.DEEPL_API_KEY) {
    throw new Error("DEEPL_API_KEY 없음");
  }

  const params = new URLSearchParams();
  params.append("text", text);
  params.append("target_lang", target.toUpperCase());
  if (source.toLowerCase() !== "auto") {
    params.append("source_lang", source.toUpperCase());
  }

  const response = await fetch("https://api-free.deepl.com/v2/translate", {
    method: "POST",
    headers: {
      Authorization: `DeepL-Auth-Key ${env.DEEPL_API_KEY}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: params.toString(),
  });

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`deepl 실패: HTTP ${response.status} ${body.slice(0, 120)}`);
  }

  const data = (await response.json()) as any;
  const translated = String(data?.translations?.[0]?.text || "").trim();
  if (!translated) {
    throw new Error("deepl 응답 파싱 실패");
  }
  return translated;
}

async function runCliWithStdin(
  bin: string,
  args: string[],
  input: string,
  timeoutMs: number,
): Promise<string> {
  return await new Promise((resolve, reject) => {
    const child = spawn(bin, args, {
      stdio: ["pipe", "pipe", "pipe"],
    });
    let stdout = "";
    let stderr = "";
    let killedByTimeout = false;

    const timer = setTimeout(() => {
      killedByTimeout = true;
      child.kill("SIGKILL");
    }, timeoutMs);

    child.stdout.on("data", (buf) => {
      stdout += String(buf);
    });
    child.stderr.on("data", (buf) => {
      stderr += String(buf);
    });
    child.on("error", (error) => {
      clearTimeout(timer);
      reject(error);
    });
    child.on("close", (code) => {
      clearTimeout(timer);
      if (killedByTimeout) {
        reject(new Error(`deepl-cli timeout ${timeoutMs}ms`));
        return;
      }
      if (code !== 0) {
        reject(new Error(`deepl-cli exit=${code} ${stderr.slice(0, 160)}`));
        return;
      }
      resolve(stdout.trim());
    });

    child.stdin.write(input);
    child.stdin.end();
  });
}

async function translateDeepLCli(text: string, source: string, target: string): Promise<string> {
  if (source === "auto") {
    throw new Error("deepl-cli는 source=auto를 지원하지 않습니다. source 언어를 명시하세요.");
  }
  const args = ["-s", "-F", source, "-T", target, "-t", String(Math.min(env.DEEPL_CLI_TIMEOUT_MS, 30000))];
  const output = await runCliWithStdin(env.DEEPL_CLI_BIN, args, text, env.DEEPL_CLI_TIMEOUT_MS);
  if (!output) {
    throw new Error("deepl-cli 응답 비어있음");
  }
  return output;
}

export const translateService = {
  getMaxInputChars() {
    return MAX_INPUT_CHARS;
  },

  async translate(text: string, source = "auto", target = "ko"): Promise<TranslateResult> {
    const input = normalizeText(text);
    if (!input) throw new Error("번역할 텍스트가 비었습니다.");
    if (input.length > MAX_INPUT_CHARS) {
      throw new Error(`입력 길이 초과: ${input.length}자 (최대 ${MAX_INPUT_CHARS}자)`);
    }

    const sourceNorm = String(source || "auto").toLowerCase();
    const targetNorm = String(target || "ko").toLowerCase();
    const chunks = splitText(input, CHUNK_SIZE);

    const engines: Array<{
      name: "deepl" | "deepl_web_cli" | "google_web_v2";
      fn: (chunk: string, source: string, target: string) => Promise<string>;
      enabled: boolean;
    }> = [
      { name: "deepl", fn: translateDeepL, enabled: Boolean(env.DEEPL_API_KEY) },
      { name: "deepl_web_cli", fn: translateDeepLCli, enabled: sourceNorm !== "auto" },
      { name: "google_web_v2", fn: translateGoogleWebV2, enabled: true },
    ];

    for (const engine of engines) {
      if (!engine.enabled) continue;
      try {
        const outputs: string[] = [];
        for (const chunk of chunks) {
          outputs.push(await engine.fn(chunk, sourceNorm, targetNorm));
        }
        return {
          translatedText: outputs.join("\n").trim(),
          engine: engine.name,
          inputChars: input.length,
          chunkCount: chunks.length,
        };
      } catch (error) {
        const err = toErrorInfo(error);
        logger.warn(
          { engine: engine.name, errorMessage: err.message, errorStack: err.stack },
          "Translate engine failed, fallback next",
        );
      }
    }

    throw new Error("모든 번역 엔진이 실패했습니다.");
  },
};

export type { TranslateResult };
