const { WebSocketServer, WebSocket } = require('ws');
const http = require('http');
const { RoomManager } = require('./rooms');
const { RelayHandler } = require('./relay');

const PORT = process.env.PORT || 3000;

function log(tag, msg) {
  const ts = new Date().toISOString().slice(11, 23);
  console.log(`[${ts}] [${tag}] ${msg}`);
}

const server = http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      status: 'ok',
      rooms: roomManager.getRoomCount(),
      connections: wss.clients.size,
      uptime: process.uptime()
    }));
    return;
  }

  if (req.url === '/stats') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(roomManager.getStats()));
    return;
  }

  res.writeHead(404);
  res.end('Not found');
});

const wss = new WebSocketServer({ server });
const roomManager = new RoomManager();
const relayHandler = new RelayHandler(roomManager);

wss.on('connection', (ws) => {
  ws.isAlive = true;
  ws.playerId = null;
  ws.roomCode = null;
  ws.playerName = null;
  ws.isRelaying = false;

  log('CONN', `New WebSocket connection. Total clients: ${wss.clients.size}`);

  ws.on('pong', () => { ws.isAlive = true; });

  ws.on('message', (data, isBinary) => {
    try {
      if (isBinary) {
        relayHandler.handlePacket(ws, data);
        return;
      }

      const msg = JSON.parse(data.toString());
      log('MSG', `player=${ws.playerId} room=${ws.roomCode} type=${msg.type}`);
      handleMessage(ws, msg);
    } catch (e) {
      log('ERR', `Bad message from player=${ws.playerId}: ${e.message}`);
      sendError(ws, 'Invalid message format');
    }
  });

  ws.on('close', (code, reason) => {
    log('CLOSE', `player=${ws.playerId} room=${ws.roomCode} name=${ws.playerName} code=${code} reason=${reason || 'none'}`);
    if (ws.roomCode) {
      const room = roomManager.getRoom(ws.roomCode);
      if (room) {
        room.removePlayer(ws.playerId);
        if (room.isEmpty()) {
          log('ROOM', `Room ${ws.roomCode} deleted (empty)`);
          roomManager.deleteRoom(ws.roomCode);
        } else {
          log('ROOM', `Room ${ws.roomCode} now has ${room.getPlayerCount()} players`);
          broadcastLobbyState(room);
        }
      }
    }
  });

  ws.on('error', (err) => {
    log('WS_ERR', `player=${ws.playerId} room=${ws.roomCode}: ${err.message}`);
  });
});

// Ping/pong keepalive
setInterval(() => {
  let terminated = 0;
  wss.clients.forEach((ws) => {
    if (!ws.isAlive) {
      log('PING', `Terminating dead connection: player=${ws.playerId} room=${ws.roomCode}`);
      ws.terminate();
      terminated++;
      return;
    }
    ws.isAlive = false;
    ws.ping();
  });
  if (terminated > 0) log('PING', `Terminated ${terminated} dead connections`);
}, 30000);

// Room cleanup (stale rooms older than 30 min)
setInterval(() => {
  roomManager.cleanupStale(30 * 60 * 1000);
}, 60000);

function handleMessage(ws, msg) {
  switch (msg.type) {
    case 'create_room': handleCreateRoom(ws, msg); break;
    case 'join_room': handleJoinRoom(ws, msg); break;
    case 'set_ready': handleSetReady(ws, msg); break;
    case 'swap_role': handleSwapRole(ws, msg); break;
    case 'start_game': handleStartGame(ws, msg); break;
    case 'set_name': handleSetName(ws, msg); break;
    case 'leave_room': handleLeaveRoom(ws); break;
    case 'return_to_lobby': handleReturnToLobby(ws); break;
    default: sendError(ws, `Unknown message type: ${msg.type}`);
  }
}

function handleCreateRoom(ws, msg) {
  if (ws.roomCode) {
    sendError(ws, 'Already in a room');
    return;
  }

  const room = roomManager.createRoom();
  const playerId = room.addPlayer(ws, msg.name || 'Host', true);

  ws.playerId = playerId;
  ws.roomCode = room.code;
  ws.playerName = msg.name || 'Host';

  send(ws, {
    type: 'room_created',
    roomCode: room.code,
    playerId: playerId
  });

  broadcastLobbyState(room);
}

function handleJoinRoom(ws, msg) {
  if (ws.roomCode) {
    sendError(ws, 'Already in a room');
    return;
  }

  const code = (msg.roomCode || '').toUpperCase().trim();
  const room = roomManager.getRoom(code);

  if (!room) {
    sendError(ws, 'Room not found');
    return;
  }

  if (room.state === 'playing') {
    sendError(ws, 'Game already in progress');
    return;
  }

  if (room.getPlayerCount() >= 4) {
    sendError(ws, 'Room is full');
    return;
  }

  const playerId = room.addPlayer(ws, msg.name || `Player ${room.getPlayerCount()}`, false);

  ws.playerId = playerId;
  ws.roomCode = code;
  ws.playerName = msg.name || `Player ${room.getPlayerCount()}`;

  send(ws, {
    type: 'room_joined',
    roomCode: code,
    playerId: playerId
  });

  broadcastLobbyState(room);
}

function handleSetReady(ws, msg) {
  const room = roomManager.getRoom(ws.roomCode);
  if (!room || room.state !== 'lobby') return;

  room.setReady(ws.playerId, msg.ready);
  broadcastLobbyState(room);
}

function handleSetName(ws, msg) {
  const room = roomManager.getRoom(ws.roomCode);
  if (!room) return;

  room.setName(ws.playerId, msg.name);
  ws.playerName = msg.name;
  broadcastLobbyState(room);
}

function handleSwapRole(ws, msg) {
  const room = roomManager.getRoom(ws.roomCode);
  if (!room || room.state !== 'lobby') return;

  room.swapRole(ws.playerId);
  broadcastLobbyState(room);
}

function handleStartGame(ws, msg) {
  const room = roomManager.getRoom(ws.roomCode);
  if (!room || room.state !== 'lobby') return;

  const player = room.getPlayer(ws.playerId);
  if (!player || !player.isHost) {
    sendError(ws, 'Only host can start');
    return;
  }

  if (!room.canStart()) {
    sendError(ws, 'Not all players ready or need at least 2 players');
    return;
  }

  room.state = 'starting';
  broadcastLobbyState(room);

  // 3 second countdown
  let countdown = 3;
  const interval = setInterval(() => {
    countdown--;
    broadcast(room, { type: 'countdown', seconds: countdown });

    if (countdown <= 0) {
      clearInterval(interval);
      room.state = 'playing';

      // Tell everyone to connect to relay mode
      broadcast(room, {
        type: 'game_start',
        players: room.getPlayersData(),
        hostId: room.getHostId()
      });
    }
  }, 1000);
}

function handleLeaveRoom(ws) {
  const room = roomManager.getRoom(ws.roomCode);
  if (!room) return;

  room.removePlayer(ws.playerId);
  ws.roomCode = null;
  ws.playerId = null;

  send(ws, { type: 'left_room' });

  if (room.isEmpty()) {
    roomManager.deleteRoom(room.code);
  } else {
    broadcastLobbyState(room);
  }
}

function handleReturnToLobby(ws) {
  const room = roomManager.getRoom(ws.roomCode);
  if (!room) return;

  const player = room.getPlayer(ws.playerId);
  if (!player || !player.isHost) return;

  room.state = 'lobby';
  room.resetReady();
  room.players.forEach(p => { p.ws.isRelaying = false; });
  broadcastLobbyState(room);
}

function broadcastLobbyState(room) {
  broadcast(room, {
    type: 'lobby_state',
    state: room.state,
    roomCode: room.code,
    players: room.getPlayersData()
  });
}

function broadcast(room, msg) {
  const data = JSON.stringify(msg);
  room.players.forEach(p => {
    if (p.ws.readyState === WebSocket.OPEN) {
      p.ws.send(data);
    }
  });
}

function send(ws, msg) {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(msg));
  }
}

function sendError(ws, error) {
  send(ws, { type: 'error', error });
}

server.listen(PORT, () => {
  console.log(`IStalk Relay Server running on port ${PORT}`);
});
