const http = require("http");
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");

const port = Number(process.env.PORT || 5177);
const root = __dirname;
const licensePath = path.join(root, "license.json");
const licenseSecret = "OBS-BASEBALL-OVERLAY-2026-LICENSE-SECRET-v1";
let scoreboardState = null;
const clients = new Set();

const types = {
  ".html": "text/html; charset=utf-8",
  ".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"
};

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://localhost:${port}`);

  if (url.pathname === "/api/state" && req.method === "GET") {
    res.writeHead(200, {
      "Content-Type": "application/json; charset=utf-8",
      "Cache-Control": "no-store"
    });
    res.end(JSON.stringify(scoreboardState));
    return;
  }

  if (url.pathname === "/api/state" && req.method === "POST") {
    let body = "";
    req.on("data", (chunk) => {
      body += chunk;
      if (body.length > 6_000_000) req.destroy();
    });
    req.on("end", () => {
      try {
        scoreboardState = JSON.parse(body);
        broadcast(scoreboardState);
        res.writeHead(204);
        res.end();
      } catch {
        res.writeHead(400);
        res.end("Bad JSON");
      }
    });
    return;
  }

  if (url.pathname === "/api/license" && req.method === "GET") {
    res.writeHead(200, {
      "Content-Type": "application/json; charset=utf-8",
      "Cache-Control": "no-store"
    });
    res.end(JSON.stringify(getLicenseStatus()));
    return;
  }

  if (url.pathname === "/api/license" && req.method === "POST") {
    let body = "";
    req.on("data", (chunk) => {
      body += chunk;
      if (body.length > 100_000) req.destroy();
    });
    req.on("end", () => {
      fs.writeFileSync(licensePath, body, "utf8");
      res.writeHead(200, {
        "Content-Type": "application/json; charset=utf-8",
        "Cache-Control": "no-store"
      });
      res.end(JSON.stringify(getLicenseStatus()));
    });
    return;
  }

  if (url.pathname === "/events") {
    res.writeHead(200, {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-store",
      Connection: "keep-alive"
    });
    clients.add(res);
    res.write(`data: ${JSON.stringify(scoreboardState)}\n\n`);
    req.on("close", () => clients.delete(res));
    return;
  }

  const safePath = path
    .normalize(decodeURIComponent(url.pathname))
    .replace(/^(\.\.[/\\])+/, "");
  const filePath = path.join(root, safePath === "/" ? "controller.html" : safePath);

  if (!filePath.startsWith(root)) {
    res.writeHead(403);
    res.end("Forbidden");
    return;
  }

  if (!getLicenseStatus().valid && isProtectedPath(url.pathname)) {
    res.writeHead(302, { Location: "/license.html" });
    res.end();
    return;
  }

  fs.readFile(filePath, (error, data) => {
    if (error) {
      res.writeHead(404);
      res.end("Not found");
      return;
    }

    res.writeHead(200, {
      "Content-Type": types[path.extname(filePath)] || "application/octet-stream",
      "Cache-Control": "no-store"
    });
    res.end(data);
  });
});

function broadcast(state) {
  const payload = `data: ${JSON.stringify(state)}\n\n`;
  for (const client of clients) {
    client.write(payload);
  }
}

function isProtectedPath(urlPath) {
  if (urlPath === "/license.html" || urlPath === "/styles.css" || urlPath === "/scoreboard.js") return false;
  if (urlPath.startsWith("/assets/") || urlPath.startsWith("/api/")) return false;
  return urlPath === "/" || urlPath.endsWith(".html");
}

function getLicenseStatus() {
  if (!fs.existsSync(licensePath)) {
    return { valid: false, licensee: "", expires: "", message: "Licenza non attivata" };
  }

  try {
    const license = JSON.parse(fs.readFileSync(licensePath, "utf8"));
    const licensee = String(license.licensee || "");
    const expires = String(license.expires || "");
    const key = String(license.key || "");
    if (!licensee || !expires || !key) {
      return { valid: false, licensee, expires, message: "Licenza incompleta" };
    }
    const expiresAt = new Date(`${expires}T23:59:59`);
    if (Number.isNaN(expiresAt.getTime())) {
      return { valid: false, licensee, expires, message: "Data licenza non valida" };
    }
    if (expiresAt < new Date()) {
      return { valid: false, licensee, expires, message: "Licenza scaduta" };
    }
    const expected = generateLicenseKey(licensee, expires);
    if (expected.toUpperCase() !== key.toUpperCase()) {
      return { valid: false, licensee, expires, message: "Codice licenza non valido" };
    }
    return { valid: true, licensee, expires, message: "Licenza valida" };
  } catch {
    return { valid: false, licensee: "", expires: "", message: "Licenza non leggibile" };
  }
}

function generateLicenseKey(licensee, expires) {
  return crypto
    .createHmac("sha256", licenseSecret)
    .update(`${String(licensee).trim().toUpperCase()}|${String(expires).trim()}`)
    .digest("hex")
    .slice(0, 24)
    .toUpperCase();
}

server.listen(port, () => {
  console.log(`Controller: http://localhost:${port}/controller.html`);
  console.log(`Overlay OBS: http://localhost:${port}/overlay.html`);
});
