#!/usr/bin/env node
"use strict";

const http = require("http");
const fs = require("fs");
const path = require("path");
const { spawn } = require("child_process");
const { loadConfig, getDefaults } = require("./config-loader");
const { analyzeScene } = require("./llm-client");
const { runGenerator, readDirectory } = require("./generator-runner");

let config;
let defaults;
try {
  config = loadConfig();
  defaults = getDefaults();
  console.log(`[config] Loaded from: ${config.configPath}`);
  console.log(`[config] Project root: ${config.projectRoot}`);
} catch (err) {
  console.error(`[error] Failed to load config: ${err.message}`);
  process.exit(1);
}

const PORT = parseInt(process.env.SG_SCENE_GENERATOR_PORT, 10) || 3210;
const HOST = "127.0.0.1";

const MIME_TYPES = {
  ".html": "text/html; charset=utf-8",
  ".js": "application/javascript",
  ".css": "text/css",
  ".json": "application/json",
};

function serveStatic(res, filePath) {
  const ext = path.extname(filePath);
  const contentType = MIME_TYPES[ext] || "application/octet-stream";

  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404);
      res.end("Not found");
      return;
    }
    res.writeHead(200, { "Content-Type": contentType });
    res.end(data);
  });
}

function initSSE(res) {
  res.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
    "Access-Control-Allow-Origin": "*",
  });
  res.write(":\n");
  return res;
}

function writeSSE(res, event, data) {
  res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
}

function parseBody(req) {
  return new Promise((resolve, reject) => {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    req.on("end", () => {
      try {
        resolve(JSON.parse(body));
      } catch (err) {
        reject(new Error("Invalid JSON"));
      }
    });
    req.on("error", reject);
  });
}

async function handleAnalyze(req, res) {
  let body;
  try {
    body = await parseBody(req);
  } catch {
    res.writeHead(400, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ error: "Invalid JSON body" }));
    return;
  }

  const sourceDir = (body.sourceDir || "").replace(/\\/g, "/");
  if (!sourceDir) {
    res.writeHead(400, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ error: "sourceDir is required" }));
    return;
  }

  let dirContents;
  try {
    dirContents = readDirectory(sourceDir);
  } catch (err) {
    res.writeHead(400, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ error: err.message }));
    return;
  }

  try {
    const result = await analyzeScene(sourceDir, dirContents, config);
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify(result));
  } catch (err) {
    res.writeHead(502, { "Content-Type": "application/json" });
    res.end(
      JSON.stringify({
        error: `LLM analysis failed: ${err.message}`,
        hint: "You can still enter scene-id and scene-name manually",
      })
    );
  }
}

async function handleGenerate(req, res) {
  let body;
  try {
    body = await parseBody(req);
  } catch {
    res.writeHead(400, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ error: "Invalid JSON body" }));
    return;
  }

  const { sourceDir, sceneId, sceneName, sceneKind, targetUrl, outputRoot, lessons } = body;
  if (!sourceDir || !sceneId || !sceneName || !outputRoot || !lessons) {
    res.writeHead(400, { "Content-Type": "application/json" });
    res.end(
      JSON.stringify({
        error:
          "All fields required: sourceDir, sceneId, sceneName, outputRoot, lessons",
      })
    );
    return;
  }

  const sseWriter = initSSE(res);

  try {
    await runGenerator(
      { sourceDir, sceneId, sceneName, sceneKind, targetUrl, outputRoot, lessons },
      sseWriter,
      config.projectRoot
    );
  } catch (err) {
    writeSSE(sseWriter, "error", { message: `Server error: ${err.message}` });
  }

  sseWriter.end();
}

function handleHealth(req, res) {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(
    JSON.stringify({
      status: "ok",
      pid: process.pid,
      configLoaded: true,
      configPath: config.configPath,
      projectRoot: config.projectRoot,
    })
  );
}

/**
 * Open a native Windows folder selection dialog using PowerShell.
 * Returns the selected folder path or null if cancelled.
 */
function openFolderDialog(defaultPath) {
  return new Promise((resolve) => {
    const psScript = `
Add-Type -AssemblyName System.Windows.Forms
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
$dialog.Description = "选择文件夹"
$dialog.ShowNewFolderButton = true
${defaultPath ? `$dialog.SelectedPath = '${defaultPath.replace(/'/g, "''")}'` : ""}
if ($dialog.ShowDialog() -eq 'OK') {
  Write-Output $dialog.SelectedPath
}
`.trim();

    const ps = spawn("powershell.exe", [
      "-NoProfile",
      "-NonInteractive",
      "-Command",
      psScript,
    ]);

    let output = "";
    let error = "";

    ps.stdout.on("data", (data) => {
      output += data.toString();
    });

    ps.stderr.on("data", (data) => {
      error += data.toString();
    });

    ps.on("close", (code) => {
      if (code === 0 && output.trim()) {
        resolve(output.trim());
      } else {
        resolve(null);
      }
    });

    ps.on("error", () => {
      resolve(null);
    });
  });
}

async function handleSelectFolder(req, res) {
  let body = {};
  try {
    body = await parseBody(req);
  } catch {
    // ignore parse error, use empty body
  }

  const selectedPath = await openFolderDialog(body.defaultPath || "");
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ path: selectedPath }));
}

async function handleSelectFile(req, res) {
  let body = {};
  try {
    body = await parseBody(req);
  } catch {
    // ignore parse error
  }

  const filter = body.filter || "所有文件 (*.*)|*.*";
  const psScript = `
Add-Type -AssemblyName System.Windows.Forms
$dialog = New-Object System.Windows.Forms.OpenFileDialog
$dialog.Filter = '${filter}'
$dialog.Title = "选择文件"
${body.defaultPath ? `$dialog.InitialDirectory = '${body.defaultPath.replace(/'/g, "''")}'` : ""}
if ($dialog.ShowDialog() -eq 'OK') {
  Write-Output $dialog.FileName
}
`.trim();

  return new Promise((resolve) => {
    const ps = spawn("powershell.exe", [
      "-NoProfile",
      "-NonInteractive",
      "-Command",
      psScript,
    ]);

    let output = "";

    ps.stdout.on("data", (data) => {
      output += data.toString();
    });

    ps.on("close", (code) => {
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ path: code === 0 && output.trim() ? output.trim() : null }));
      resolve();
    });

    ps.on("error", () => {
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ path: null }));
      resolve();
    });
  });
}

const server = http.createServer(async (req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const pathname = url.pathname;

  if (req.method === "OPTIONS") {
    res.writeHead(204, {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type",
    });
    res.end();
    return;
  }

  try {
    if (pathname === "/health" && req.method === "GET") {
      handleHealth(req, res);
    } else if (pathname === "/analyze" && req.method === "POST") {
      await handleAnalyze(req, res);
    } else if (pathname === "/generate" && req.method === "POST") {
      await handleGenerate(req, res);
    } else if (pathname === "/select-folder" && req.method === "POST") {
      await handleSelectFolder(req, res);
    } else if (pathname === "/select-file" && req.method === "POST") {
      await handleSelectFile(req, res);
    } else if (pathname === "/" || pathname === "/index.html") {
      serveStatic(res, path.join(__dirname, "sg_scene_generator.html"));
    } else {
      const filePath = path.resolve(__dirname, pathname);
      const resolvedDir = path.resolve(__dirname);
      if (!filePath.startsWith(resolvedDir + path.sep) && filePath !== resolvedDir) {
        res.writeHead(403, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ error: "Forbidden" }));
        return;
      }
      if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
        serveStatic(res, filePath);
      } else {
        res.writeHead(404, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ error: "Not found" }));
      }
    }
  } catch (err) {
    console.error(`[error] ${req.method} ${pathname}: ${err.message}`);
    if (!res.headersSent) {
      res.writeHead(500, { "Content-Type": "application/json" });
    }
    res.end(JSON.stringify({ error: err.message }));
  }
});

server.listen(PORT, HOST, () => {
  console.log("");
  console.log("  ╔══════════════════════════════════════════════════╗");
  console.log("  ║   sgClaw · Scene Skill Generator                 ║");
  console.log("  ╠══════════════════════════════════════════════════╣");
  console.log("  ║                                                  ║");
  console.log(`  ║   访问地址:   http://${HOST}:${PORT}/              ║`);
  console.log("  ║                                                  ║");
  console.log("  ║   按 Ctrl+C 停止服务                              ║");
  console.log("  ╚══════════════════════════════════════════════════╝");
  console.log("");
});

process.on("SIGINT", () => {
  console.log("\n[info] Shutting down...");
  server.close(() => process.exit(0));
});
