const express = require('express');
const http = require('http');
const cors = require('cors');
const { Server } = require('socket.io');

const PORT = process.env.PORT || 3001;
const ALLOWED_ORIGIN = process.env.ALLOWED_ORIGIN || '*';
const SOCKET_IO_PATH = process.env.SOCKET_IO_PATH || '/webrtc/socket.io';

const app = express();

app.use(cors({
  origin: ALLOWED_ORIGIN,
  methods: ['GET', 'POST'],
  credentials: false
}));

app.use(express.json());

function healthCheck(req, res) {
  res.setHeader('Content-Type', 'application/json');
  res.status(200).json({
    ok: true,
    service: 'USHS WebRTC signaling server',
    socketPath: SOCKET_IO_PATH,
    path: req.path,
    timestamp: new Date().toISOString()
  });
}

// Health check routes for cPanel / browser testing.
app.get('/', healthCheck);
app.get('/webrtc', healthCheck);
app.get('/webrtc/', healthCheck);
app.get('/health', healthCheck);
app.get('/status', healthCheck);

// Fallback for normal browser checks, but do not interrupt Socket.IO.
app.use((req, res, next) => {
  if (req.path.startsWith(SOCKET_IO_PATH) || req.path.startsWith('/socket.io')) {
    return next();
  }

  res.setHeader('Content-Type', 'application/json');
  return res.status(200).json({
    ok: true,
    service: 'USHS WebRTC signaling server',
    socketPath: SOCKET_IO_PATH,
    path: req.path,
    message: 'WebRTC signaling server is running'
  });
});

const server = http.createServer(app);

const io = new Server(server, {
  path: SOCKET_IO_PATH,
  cors: {
    origin: ALLOWED_ORIGIN,
    methods: ['GET', 'POST']
  },
  transports: ['websocket', 'polling']
});

const rooms = new Map();

function getRoom(roomToken) {
  if (!rooms.has(roomToken)) {
    rooms.set(roomToken, {
      customerSocketId: null,
      csrSocketId: null,
      participants: new Map()
    });
  }

  return rooms.get(roomToken);
}

function emitRoomUpdate(roomToken) {
  const room = rooms.get(roomToken);

  if (!room) return;

  io.to(roomToken).emit('participant-update', {
    hasCustomer: Boolean(room.customerSocketId),
    hasCsr: Boolean(room.csrSocketId),
    participantCount: room.participants.size
  });
}

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

  socket.on('join-room', (data) => {
    const roomToken = data && data.roomToken;
    const userId = data && data.userId;
    const participantType = data && data.participantType;

    if (!roomToken || !participantType) {
      socket.emit('server-error', { message: 'Missing roomToken or participantType.' });
      return;
    }

    if (!['customer', 'csr'].includes(participantType)) {
      socket.emit('server-error', { message: 'Invalid participantType.' });
      return;
    }

    const room = getRoom(roomToken);

    socket.join(roomToken);
    socket.data.roomToken = roomToken;
    socket.data.participantType = participantType;
    socket.data.userId = userId || null;

    room.participants.set(socket.id, { userId: userId || null, participantType });

    if (participantType === 'customer') {
      room.customerSocketId = socket.id;
    }

    if (participantType === 'csr') {
      room.csrSocketId = socket.id;
    }

    console.log(`${participantType} joined room ${roomToken}:`, socket.id);

    emitRoomUpdate(roomToken);

    // When both customer and CSR are inside the room, CSR creates the WebRTC offer.
    if (room.customerSocketId && room.csrSocketId) {
      io.to(room.csrSocketId).emit('ready-for-offer');
    }
  });

  socket.on('offer', (data) => {
    const roomToken = data && data.roomToken;
    const offer = data && data.offer;
    if (!roomToken || !offer) return;
    socket.to(roomToken).emit('offer', offer);
  });

  socket.on('answer', (data) => {
    const roomToken = data && data.roomToken;
    const answer = data && data.answer;
    if (!roomToken || !answer) return;
    socket.to(roomToken).emit('answer', answer);
  });

  socket.on('ice-candidate', (data) => {
    const roomToken = data && data.roomToken;
    const candidate = data && data.candidate;
    if (!roomToken || !candidate) return;
    socket.to(roomToken).emit('ice-candidate', candidate);
  });

  socket.on('end-call', (data) => {
    const roomToken = data && data.roomToken;
    if (!roomToken) return;
    socket.to(roomToken).emit('call-ended');
    console.log('Call ended in room:', roomToken);
  });

  socket.on('disconnect', () => {
    const roomToken = socket.data.roomToken;

    if (!roomToken || !rooms.has(roomToken)) {
      console.log('Socket disconnected:', socket.id);
      return;
    }

    const room = rooms.get(roomToken);
    const participantType = socket.data.participantType;

    room.participants.delete(socket.id);

    if (room.customerSocketId === socket.id) {
      room.customerSocketId = null;
    }

    if (room.csrSocketId === socket.id) {
      room.csrSocketId = null;
    }

    socket.to(roomToken).emit('participant-left', { participantType });

    console.log(`${participantType || 'participant'} left room ${roomToken}:`, socket.id);

    if (room.participants.size === 0) {
      rooms.delete(roomToken);
      console.log('Room deleted:', roomToken);
    } else {
      emitRoomUpdate(roomToken);
    }
  });
});

server.listen(PORT, () => {
  console.log(`USHS WebRTC signaling server running on port ${PORT}`);
  console.log(`Socket.IO path: ${SOCKET_IO_PATH}`);
});
