const path = require("path");
const { spawn } = require("child_process");

const WORKER = path.join(__dirname, "command-preview-worker.js");
const PREVIEW_TIMEOUT_MS = 1200;
const MAX_PREVIEW_LENGTH = 2000;

async function generateCommandPreview({ code, language = "js" }) {
  const generatedAt = Date.now();
  try {
    const raw = await runPreviewWorker({
      code: String(code || ""),
      language: language === "python" ? "python" : "js"
    });
    const previewText = normalizePreviewText(raw).slice(0, MAX_PREVIEW_LENGTH);
    return {
      preview_text: previewText,
      preview_status: "ready",
      preview_error: null,
      preview_generated_at: generatedAt,
      preview_dynamic_segments: JSON.stringify(findDynamicSegments(previewText))
    };
  } catch (error) {
    return {
      preview_text: null,
      preview_status: "unavailable",
      preview_error: cleanError(error),
      preview_generated_at: generatedAt,
      preview_dynamic_segments: "[]"
    };
  }
}

function runPreviewWorker(payload) {
  return new Promise((resolve, reject) => {
    const child = spawn(process.execPath, ["--max-old-space-size=32", WORKER], {
      stdio: ["pipe", "pipe", "pipe"],
      windowsHide: true,
      env: {}
    });
    let stdout = "";
    let stderr = "";
    let settled = false;
    const finish = (callback) => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      callback();
    };
    const timer = setTimeout(() => {
      child.kill("SIGKILL");
      finish(() => reject(new Error("Command preview timed out.")));
    }, PREVIEW_TIMEOUT_MS);
    child.stdout.on("data", (chunk) => {
      stdout += chunk.toString();
      if (stdout.length > 10000) child.kill("SIGKILL");
    });
    child.stderr.on("data", (chunk) => {
      stderr += chunk.toString();
    });
    child.on("error", (error) => finish(() => reject(error)));
    child.on("close", () => finish(() => {
      try {
        const result = JSON.parse(stdout || "{}");
        if (!result.ok) throw new Error(result.error || stderr || "Preview failed.");
        resolve(String(result.output || ""));
      } catch (error) {
        reject(error);
      }
    }));
    child.stdin.end(JSON.stringify(payload));
  });
}

function normalizePreviewText(value) {
  return String(value || "")
    .replace(/\bSome[ _-]?User\b/gi, "some_user")
    .replace(/\bsome[ -]user\b/gi, "some_user")
    .replace(/\r\n/g, "\n")
    .trim();
}

function findDynamicSegments(value) {
  const text = String(value || "");
  const pattern = /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z\b|\bsome_user\b|\b(?:user|platform|message|channel|guild|item)_\d+\b|\b\d+(?:\.\d+)?\b/g;
  const segments = [];
  let match;
  while ((match = pattern.exec(text))) {
    segments.push({
      start: match.index,
      end: match.index + match[0].length,
      type: dynamicType(match[0])
    });
  }
  return segments;
}

function previewParts(value, dynamicSegments, limit = null) {
  const source = String(value || "");
  const text = limit && source.length > limit ? `${source.slice(0, Math.max(1, limit - 1))}…` : source;
  const segments = parseSegments(dynamicSegments)
    .filter((segment) => segment.start < text.length)
    .map((segment) => ({ ...segment, end: Math.min(segment.end, text.length) }));
  const parts = [];
  let offset = 0;
  for (const segment of segments) {
    if (segment.start < offset || segment.end <= segment.start) continue;
    if (segment.start > offset) parts.push({ text: text.slice(offset, segment.start), dynamic: false });
    parts.push({
      text: text.slice(segment.start, segment.end),
      dynamic: true,
      type: segment.type
    });
    offset = segment.end;
  }
  if (offset < text.length) parts.push({ text: text.slice(offset), dynamic: false });
  return parts;
}

function parseSegments(value) {
  try {
    const parsed = typeof value === "string" ? JSON.parse(value) : value;
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

function dynamicType(value) {
  if (value === "some_user") return "username";
  if (/^\d{4}-\d{2}-\d{2}T/.test(value)) return "date";
  if (/^[a-z]+_\d+$/i.test(value)) return "id";
  return "number";
}

function cleanError(error) {
  return String(error?.message || error || "Preview failed.")
    .replace(/\s+/g, " ")
    .slice(0, 500);
}

module.exports = {
  PREVIEW_TIMEOUT_MS,
  generateCommandPreview,
  normalizePreviewText,
  findDynamicSegments,
  previewParts
};
