import { Server as HttpServer } from "http";
import { Server, Socket } from "socket.io";

let io: Server | null = null;

interface ConnectedAgent {
  socketId: string;
  ip: string;
  connectedAt: string;
}

// Windows Agents (DentalAgent.exe) connected via the /agent namespace, keyed by socket id.
const connectedAgents = new Map<string, ConnectedAgent>();

export function getAgentStatus() {
  return {
    connected: connectedAgents.size > 0,
    agents: Array.from(connectedAgents.values()),
  };
}

export function initSocket(server: HttpServer) {
  const NODE_ENV = (
    process.env.NODE_ENV ||
    process.env.ENV ||
    "development"
  ).toLowerCase();

  const rawFrontendUrls =
    process.env.FRONTEND_URLS || process.env.FRONTEND_URL || "";
  const FRONTEND_URLS = rawFrontendUrls
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean);

  // In dev: allow all origins
  // In prod: restrict to FRONTEND_URLS if provided
  const corsOrigin =
    NODE_ENV !== "production"
      ? true
      : FRONTEND_URLS.length > 0
      ? FRONTEND_URLS
      : false; // no origins allowed if none configured in prod

  io = new Server(server, {
    cors: {
      origin: corsOrigin,
      methods: ["GET", "POST"],
      credentials: true,
    },
  });

  io.on("connection", (socket: Socket) => {
    console.log("🔌 Socket connected:", socket.id);

    socket.on("disconnect", () => {
      console.log("🔌 Socket disconnected:", socket.id);
    });
  });

  // Optional: log low-level engine errors for debugging
  io.engine.on("connection_error", (err) => {
    console.error("Socket engine connection_error:", err);
  });

  initAgentNamespace(io);

  return io;
}

// Windows Agent (DentalAgent.exe) bridge. Separate namespace so browser clients on the
// default namespace never see agent traffic, and so we can gate it with a shared secret —
// this connection will eventually be able to click/type on a real front-desk PC.
function initAgentNamespace(server: Server) {
  const agentNamespace = server.of("/agent");
  const AGENT_TOKEN = process.env.WINDOWS_AGENT_TOKEN;

  agentNamespace.use((socket, next) => {
    if (!AGENT_TOKEN) {
      // No token configured — refuse rather than silently accepting any connection.
      return next(new Error("Windows Agent bridge is not configured (WINDOWS_AGENT_TOKEN unset)"));
    }
    if (socket.handshake.auth?.token !== AGENT_TOKEN) {
      return next(new Error("Invalid agent token"));
    }
    next();
  });

  agentNamespace.on("connection", (socket: Socket) => {
    // x-forwarded-for is only trustworthy behind a proxy we control (nginx in prod);
    // falls back to the raw socket address for direct LAN connections in dev.
    const ip = (socket.handshake.headers["x-forwarded-for"] as string)?.split(",")[0]?.trim()
      || socket.handshake.address;

    connectedAgents.set(socket.id, {
      socketId: socket.id,
      ip,
      connectedAt: new Date().toISOString(),
    });
    console.log("🖥️  Windows Agent connected:", socket.id, ip);
    io?.emit("type-agent:status", getAgentStatus());

    socket.on("disconnect", () => {
      connectedAgents.delete(socket.id);
      console.log("🖥️  Windows Agent disconnected:", socket.id);
      io?.emit("type-agent:status", getAgentStatus());
    });
  });
}

export { io };
