import express, { type Express } from "express";
import cors from "cors";
import path from "path";
import { fileURLToPath } from "url";
import router from "./routes/index.js";
import adminRelayPanelRouter from "./routes/admin-relay-panel.js";
import { responseTimeMiddleware } from "./middlewares/response-time.js";

const app: Express = express();

app.set("trust proxy", 1);

// ── CORS (#5) ────────────────────────────────────────────────────────────────
// CORS_ORIGINS = comma-separated list of allowed origins.
// Default in production: alexanderwhitestone.com (and www. variant).
// Default in development: all origins permitted.
const isProd = process.env["NODE_ENV"] === "production";

const rawOrigins = process.env["CORS_ORIGINS"];
const allowedOrigins: string[] = rawOrigins
  ? rawOrigins.split(",").map((o) => o.trim()).filter(Boolean)
  : isProd
    ? [
        "https://alexanderwhitestone.com",
        "https://www.alexanderwhitestone.com",
        "https://alexanderwhitestone.ai",
        "https://www.alexanderwhitestone.ai",
        "https://hermes.tailb74b2d.ts.net",
      ]
    : [];

app.use(
  cors({
    origin:
      allowedOrigins.length === 0
        ? true
        : (origin, callback) => {
            if (!origin || allowedOrigins.includes(origin)) {
              callback(null, true);
            } else {
              callback(new Error(`CORS: origin '${origin}' not allowed`));
            }
          },
    credentials: true,
    methods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
    allowedHeaders: ["Content-Type", "Authorization", "X-Session-Token", "X-Nostr-Token"],
    exposedHeaders: ["X-Session-Token", "X-Nostr-Token"],
  }),
);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(responseTimeMiddleware);

app.use("/api", router);

// ── Relay admin panel at /admin/relay ────────────────────────────────────────
// Served outside /api so the URL is clean: /admin/relay (not /api/admin/relay).
app.use(adminRelayPanelRouter);

// ── Tower (Matrix 3D frontend) ───────────────────────────────────────────────
// Serve the pre-built Three.js world at /tower. WS client auto-connects to
// /api/ws on the same host.
//
// Path resolution strategy:
//   ESM dev (tsx ./src/index.ts): import.meta.url is a file:// URL; resolve 3
//     levels up from src/ to reach the workspace root.
//   CJS prod bundle (node dist/index.cjs): import.meta is {} (empty), so fall
//     back to process.cwd(). The run command is issued from the workspace root,
//     so process.cwd() == workspace root.
const towerDist = (() => {
  try {
    const metaUrl: string | undefined = (import.meta as { url?: string }).url;
    if (metaUrl && metaUrl.startsWith("file:")) {
      return path.resolve(path.dirname(fileURLToPath(metaUrl)), "../../..", "the-matrix", "dist");
    }
  } catch {}
  // CJS bundle: run command is `node artifacts/api-server/dist/index.cjs` from workspace root
  return path.join(process.cwd(), "the-matrix", "dist");
})();
app.use("/tower", express.static(towerDist));
app.get("/tower/*splat", (_req, res) => res.sendFile(path.join(towerDist, "index.html")));

// Vite builds asset references as absolute /assets/... paths.
// Mirror them at the root so the browser can load them from /tower.
app.use("/assets", express.static(path.join(towerDist, "assets")));
app.use("/sw.js", express.static(path.join(towerDist, "sw.js")));
app.use("/manifest.json", express.static(path.join(towerDist, "manifest.json")));

app.get("/", (_req, res) => {
  res.setHeader("Content-Type", "text/html");
  res.send(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Alexander Whitestone</title>
<style>
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  html, body {
    height: 100%;
    background: #050508;
    color: #e8e8f0;
    font-family: 'SF Mono', 'Fira Code', 'Courier New', monospace;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }
  canvas {
    position: fixed;
    inset: 0;
    z-index: 0;
    opacity: 0.18;
  }
  main {
    position: relative;
    z-index: 1;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 40px;
  }
  h1 {
    font-family: system-ui, sans-serif;
    font-size: clamp(1.4rem, 4vw, 2.4rem);
    font-weight: 300;
    letter-spacing: 0.25em;
    text-transform: uppercase;
    color: #c8c8d8;
  }
  h1 em {
    font-style: normal;
    color: #f7931a;
  }
  p {
    color: #44445a;
    font-size: 0.78rem;
    letter-spacing: 0.15em;
    max-width: 320px;
    line-height: 1.8;
  }
  a.enter {
    display: inline-block;
    padding: 14px 48px;
    border: 1px solid #2a2a3a;
    border-radius: 4px;
    color: #6b6b80;
    font-size: 0.72rem;
    letter-spacing: 0.3em;
    text-transform: uppercase;
    text-decoration: none;
    transition: border-color 0.3s, color 0.3s;
    cursor: pointer;
  }
  a.enter:hover {
    border-color: #f7931a44;
    color: #f7931a;
  }
</style>
</head>
<body>
<canvas id="c"></canvas>
<main>
  <h1>Alexander <em>Whitestone</em></h1>
  <p>AI infrastructure &amp; Lightning-native agents.</p>
  <a class="enter" href="/tower">enter</a>
</main>
<script>
  // Subtle falling-digit rain behind the landing page
  const canvas = document.getElementById('c');
  const ctx = canvas.getContext('2d');
  let cols, drops;
  function resize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    cols = Math.floor(canvas.width / 18);
    drops = Array.from({ length: cols }, () => Math.random() * -80 | 0);
  }
  resize();
  window.addEventListener('resize', resize);
  setInterval(() => {
    ctx.fillStyle = 'rgba(5,5,8,0.15)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#f7931a';
    ctx.font = '13px monospace';
    drops.forEach((y, i) => {
      const ch = Math.random() > 0.5
        ? String.fromCharCode(0x30A0 + Math.random() * 96 | 0)
        : (Math.random() * 10 | 0).toString();
      ctx.fillText(ch, i * 18, y * 18);
      if (y * 18 > canvas.height && Math.random() > 0.97) drops[i] = 0;
      else drops[i]++;
    });
  }, 60);
</script>
</body>
</html>`);
});
app.get("/api", (_req, res) => res.redirect("/api/ui"));

export default app;
