import "dotenv/config";
import WebSocket, { WebSocketServer } from "ws";
import { createServer } from "http";

const JETSTREAM_URL =
  process.env.JETSTREAM_URL || "wss://jetstream2.us-east.bsky.network/subscribe";
const PORT = parseInt(process.env.PORT || "3002", 10);
const WANTED_COLLECTIONS = [
  "app.sidetrail.trail",
  "app.sidetrail.walk",
  "app.sidetrail.completion",
];

// Track connected clients
const clients = new Set<WebSocket>();

// Jetstream connection state
let jetstreamWs: WebSocket | null = null;
let isDestroyed = false;

function buildJetstreamUrl(): string {
  const params = new URLSearchParams();
  for (const collection of WANTED_COLLECTIONS) {
    params.append("wantedCollections", collection);
  }
  return `${JETSTREAM_URL}?${params.toString()}`;
}

function broadcast(data: string): void {
  for (const client of clients) {
    if (client.readyState === WebSocket.OPEN) {
      client.send(data);
    }
  }
}

function connectToJetstream(): void {
  if (isDestroyed) return;

  const url = buildJetstreamUrl();
  console.log(`[Jetstream] Connecting to: ${url}`);

  jetstreamWs = new WebSocket(url);

  jetstreamWs.on("open", () => {
    console.log("[Jetstream] Connected");
  });

  jetstreamWs.on("message", (data) => {
    const message = data.toString();
    const parsed = JSON.parse(message);
    if (parsed.kind !== "commit") return;
    broadcast(message);
  });

  jetstreamWs.on("error", (err) => {
    console.error("[Jetstream] Error:", err.message);
  });

  jetstreamWs.on("close", (code, reason) => {
    console.log(`[Jetstream] Closed (code: ${code}, reason: ${reason})`);
    jetstreamWs = null;

    if (!isDestroyed) {
      console.log("[Jetstream] Reconnecting in 5s...");
      setTimeout(connectToJetstream, 5000);
    }
  });
}

function shutdown(): void {
  console.log("\nShutting down...");
  isDestroyed = true;

  if (jetstreamWs) {
    jetstreamWs.close();
    jetstreamWs = null;
  }

  for (const client of clients) {
    client.close();
  }
  clients.clear();

  process.exit(0);
}

// Create HTTP server for health checks
const server = createServer((req, res) => {
  if (req.url === "/health") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(
      JSON.stringify({
        status: "ok",
        clients: clients.size,
        jetstream: jetstreamWs?.readyState === WebSocket.OPEN ? "connected" : "disconnected",
      }),
    );
  } else {
    res.writeHead(404);
    res.end("Not found");
  }
});

// Create WebSocket server attached to HTTP server
const wss = new WebSocketServer({ server });

wss.on("connection", (ws, req) => {
  const clientIp = req.socket.remoteAddress;
  console.log(`[Client] Connected from ${clientIp} (total: ${clients.size + 1})`);

  clients.add(ws);

  ws.on("close", () => {
    clients.delete(ws);
    console.log(`[Client] Disconnected (total: ${clients.size})`);
  });

  ws.on("error", (err) => {
    console.error(`[Client] Error:`, err.message);
    clients.delete(ws);
  });
});

// Start server
server.listen(PORT, () => {
  console.log(`[Server] WebSocket relay listening on port ${PORT}`);
  console.log(`[Server] Health check: http://localhost:${PORT}/health`);

  // Connect to Jetstream
  connectToJetstream();
});

// Handle shutdown signals
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
