📄 index.js
import { handler } from "../build/handler.js";
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";

// Create Express app and HTTP server
const app = express();
const httpServer = createServer(app);

// Initialize Socket.IO
const io = new Server(httpServer, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

// Room state management
const rooms = new Map();
// Map of socket IDs to room codes
const socketToRoom = new Map();

// Generate a unique 6-character room code
function generateRoomCode() {
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  let code;
  do {
    code = "";
    for (let i = 0; i < 6; i++) {
      code += chars.charAt(Math.floor(Math.random() * chars.length));
    }
  } while (rooms.has(code));
  return code;
}

// Socket.IO signaling logic
io.on("connection", (socket) => {
  console.log("Client connected:", socket.id);

  // Master creates a room
  socket.on("create-room", (callback) => {
    const roomCode = generateRoomCode();
    rooms.set(roomCode, {
      master: socket.id,
      displays: [],
      createdAt: Date.now(),
    });
    socketToRoom.set(socket.id, roomCode);
    socket.join(roomCode);

    console.log(`Room created: ${roomCode} by ${socket.id}`);
    callback({ success: true, roomCode });
  });

  // Display device joins a room
  socket.on("join-room", (roomCode, callback) => {
    const room = rooms.get(roomCode);

    if (!room) {
      callback({ success: false, error: "Room not found" });
      return;
    }

    // Add display to room
    room.displays.push(socket.id);
    socketToRoom.set(socket.id, roomCode);
    socket.join(roomCode);

    // Notify master of new display
    io.to(room.master).emit("display-joined", {
      displayId: socket.id,
      totalDisplays: room.displays.length,
    });

    console.log(`Display ${socket.id} joined room ${roomCode}`);
    callback({ success: true, masterId: room.master });
  });

  // Game commands: master sends to specific display
  socket.on("game-command", ({ to, command }) => {
    io.to(to).emit("game-command", command);
  });

  // Game broadcast: master sends to all displays in room
  socket.on("game-broadcast", ({ roomCode, command }) => {
    const room = rooms.get(roomCode);
    if (room) {
      room.displays.forEach((displayId) => {
        io.to(displayId).emit("game-command", command);
      });
    }
  });

  // Display actions: display sends to master
  socket.on("display-action", (action) => {
    const roomCode = socketToRoom.get(socket.id);
    if (roomCode) {
      const room = rooms.get(roomCode);
      if (room) {
        io.to(room.master).emit("display-action", {
          from: socket.id,
          action,
        });
      }
    }
  });

  // Handle disconnections
  socket.on("disconnect", () => {
    console.log("Client disconnected:", socket.id);
    const roomCode = socketToRoom.get(socket.id);

    if (roomCode) {
      const room = rooms.get(roomCode);
      if (room) {
        // If master disconnects, close the room
        if (room.master === socket.id) {
          console.log(`Master disconnected, closing room ${roomCode}`);
          // Notify all displays
          room.displays.forEach((displayId) => {
            io.to(displayId).emit("room-closed");
            socketToRoom.delete(displayId);
          });
          rooms.delete(roomCode);
        } else {
          // Remove display from room
          room.displays = room.displays.filter((id) => id !== socket.id);
          // Notify master
          io.to(room.master).emit("display-left", {
            displayId: socket.id,
            totalDisplays: room.displays.length,
          });
        }
      }
      socketToRoom.delete(socket.id);
    }
  });
});

// SvelteKit handler MUST be added last
app.use(handler);

const PORT = process.env.PORT || 3000;
httpServer.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
  console.log(`- SvelteKit app available`);
  console.log(`- Socket.IO signaling available`);
});