import http from "node:http";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { spawn } from "node:child_process";
import worker from "../dist/server/index.js";

const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
const clientRoot = path.join(root, "dist", "client");
const args = process.argv.slice(2);
const option = (name, fallback) => {
  const index = args.indexOf(name);
  return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
};
const host = option("--host", "127.0.0.1");
const port = Number(option("--port", "8765"));
const serialPort = option("--serial", "COM7");
const commandFile = path.join(root, ".serial-command.txt");
let bridge = null;
let serialStatus = "disconnected";
let serialMessage = "";
const eventClients = new Set();

function sendEvent(type, data) {
  const payload = `event: ${type}\ndata: ${JSON.stringify(data)}\n\n`;
  for (const client of eventClients) client.write(payload);
}

function startSerialBridge() {
  if (bridge && bridge.exitCode === null) return;
  serialStatus = "connecting";
  serialMessage = `Abriendo ${serialPort}`;
  const script = path.join(root, "scripts", "serial-bridge.ps1");
  bridge = spawn("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script, "-PortName", serialPort, "-BaudRate", "9600", "-CommandFile", commandFile], { windowsHide: true });
  let output = "";
  bridge.stdout.on("data", (chunk) => {
    output += chunk.toString("utf8");
    const lines = output.split(/\r?\n/); output = lines.pop() ?? "";
    for (const line of lines.map((value) => value.trim()).filter(Boolean)) {
      if (line === "@@STATUS:CONNECTED") {
        serialStatus = "connected"; serialMessage = `${serialPort} conectado`;
        sendEvent("status", { status: serialStatus, message: serialMessage, port: serialPort });
      } else if (line.startsWith("@@STATUS:ERROR:")) {
        serialStatus = "error"; serialMessage = line.slice(15);
        sendEvent("status", { status: serialStatus, message: serialMessage, port: serialPort });
      } else sendEvent("telemetry", { line });
    }
  });
  bridge.on("exit", () => {
    bridge = null;
    if (serialStatus !== "error") { serialStatus = "disconnected"; serialMessage = `${serialPort} desconectado`; }
    sendEvent("status", { status: serialStatus, message: serialMessage, port: serialPort });
  });
}

const mime = {
  ".css": "text/css; charset=utf-8", ".js": "text/javascript; charset=utf-8",
  ".json": "application/json; charset=utf-8", ".png": "image/png",
  ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".svg": "image/svg+xml",
  ".webp": "image/webp", ".ico": "image/x-icon", ".woff2": "font/woff2",
};

async function localAsset(request) {
  const pathname = decodeURIComponent(new URL(request.url).pathname);
  const relative = pathname.replace(/^\/+/, "");
  const file = path.resolve(clientRoot, relative);
  if (!file.startsWith(clientRoot + path.sep)) return new Response("No permitido", { status: 403 });
  try {
    const body = await fs.readFile(file);
    return new Response(body, { headers: { "content-type": mime[path.extname(file).toLowerCase()] ?? "application/octet-stream", "cache-control": "no-cache" } });
  } catch {
    return new Response("No encontrado", { status: 404 });
  }
}

const assets = { fetch: localAsset };
const executionContext = { waitUntil() {}, passThroughOnException() {} };
const server = http.createServer(async (req, res) => {
  try {
    const url = new URL(req.url ?? "/", `http://${host}:${port}`);
    if (url.pathname === "/api/serial/events") {
      res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
      eventClients.add(res);
      res.write(`event: status\ndata: ${JSON.stringify({ status: serialStatus, message: serialMessage, port: serialPort })}\n\n`);
      req.on("close", () => eventClients.delete(res));
      return;
    }
    if (url.pathname === "/api/serial/status") {
      res.writeHead(200, { "content-type": "application/json", "cache-control": "no-cache" });
      res.end(JSON.stringify({ status: serialStatus, message: serialMessage, port: serialPort })); return;
    }
    if (url.pathname === "/api/serial/connect" && req.method === "POST") {
      startSerialBridge();
      res.writeHead(200, { "content-type": "application/json" });
      res.end(JSON.stringify({ status: serialStatus, port: serialPort })); return;
    }
    if (url.pathname === "/api/serial/start" && req.method === "POST") {
      if (serialStatus !== "connected") { res.writeHead(409); res.end("Arduino no conectado"); return; }
      await fs.writeFile(commandFile, "START\n", "utf8");
      res.writeHead(200, { "content-type": "application/json" }); res.end('{"ok":true}'); return;
    }
    if (url.pathname === "/api/serial/disconnect" && req.method === "POST") {
      if (bridge) bridge.kill();
      serialStatus = "disconnected"; serialMessage = `${serialPort} desconectado`;
      res.writeHead(200, { "content-type": "application/json" }); res.end('{"ok":true}'); return;
    }
    const directAsset = url.pathname.startsWith("/assets/") || /\.[a-z0-9]{2,5}$/i.test(url.pathname);
    const response = directAsset
      ? await localAsset(new Request(url))
      : await worker.fetch(new Request(url, { method: req.method }), { ASSETS: assets }, executionContext);
    res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
    res.end(Buffer.from(await response.arrayBuffer()));
  } catch (error) {
    res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
    res.end(`Error local: ${error.message}`);
  }
});

server.listen(port, host, () => {
  console.log(`Aqua Café 10B listo en http://${host}:${port}`);
  console.log("No cierres esta ventana mientras uses el dashboard.");
  startSerialBridge();
});
