'use strict';

const { Server: SocketIO } = require('socket.io');
const { verifyToken, parseCookies } = require('./auth');
const { throttle, Coalescer } = require('./util/throttle');

function buildIO(opts) {
  const { server, SESSION_SECRET } = opts;
  const io = new SocketIO(server, { cors: { origin: '*' } });

  io.use((socket, next) => {
    const cookies = parseCookies(socket.request.headers.cookie);
    if (verifyToken(cookies.auth_token, SESSION_SECRET)) return next();
    next(new Error('Auth required'));
  });

  return io;
}

function buildBroadcaster(io, state, proxyRegistry) {
  const logCoalescer = new Coalescer((entries) => {
    io.emit('log-batch', entries);
  }, 100);

  const statsThrottle = throttle(() => {
    io.emit('stats', {
      activeBots: state.activeBots.size,
      totalBotsSpawned: state.counters.totalBotsSpawned,
      totalConnectionsFailed: state.counters.totalConnectionsFailed,
      totalKeepAlivesSent: state.counters.totalKeepAlivesSent,
      totalHandshakesOk: state.counters.totalHandshakesOk,
      testRunning: state.testRunning,
      currentTarget: state.currentTarget,
      activeProxies: proxyRegistry.list({ status: 'alive' }).length,
      deadProxies: proxyRegistry.list({ status: 'dead' }).length,
      bots: state.snapshot(),
    });
  }, 250);

  const botThrottles = new Map();
  function emitBotUpdate(botId) {
    let t = botThrottles.get(botId);
    if (!t) {
      t = throttle(() => {
        const bot = state.activeBots.get(botId);
        if (bot) io.emit('bot:update', { id: botId, ...bot.toJSON() });
      }, 500);
      botThrottles.set(botId, t);
    }
    t();
  }
  function forgetBot(botId) {
    const t = botThrottles.get(botId);
    if (t) { t.cancel(); botThrottles.delete(botId); }
  }

  const metricsInterval = setInterval(() => {
    if (state.activeBots.size === 0) return;
    io.emit('test:metrics', {
      activeBots: state.activeBots.size,
      proxyAlive: proxyRegistry.list({ status: 'alive' }).length,
      proxyDead: proxyRegistry.list({ status: 'dead' }).length,
    });
  }, 1000);

  proxyRegistry.on('add', (entry) => io.emit('proxy:update', { key: entry.key, entry }));
  proxyRegistry.on('update', (entry) => io.emit('proxy:update', { key: entry.key, entry }));
  proxyRegistry.on('remove', ({ key }) => io.emit('proxy:remove', { key }));
  proxyRegistry.on('job:start', (job) => {
    state.proxyValidationJob = job;
    io.emit('proxy-progress', job);
  });
  proxyRegistry.on('job:progress', (job) => {
    state.proxyValidationJob = job;
    io.emit('proxy-progress', job);
  });
  proxyRegistry.on('job:end', (job) => {
    state.proxyValidationJob = job;
    io.emit('proxy-done', job);
  });

  return {
    emitLog(entry) {
      io.emit('log', entry);
    },
    emitBotUpdate,
    forgetBot,
    emitStats: statsThrottle,
    flushStats: statsThrottle.flush,
    logBatch: logCoalescer,
    metricsInterval,
    shutdown() {
      clearInterval(metricsInterval);
      for (const t of botThrottles.values()) t.cancel();
      botThrottles.clear();
      logCoalescer.flush();
    },
  };
}

module.exports = { buildIO, buildBroadcaster };
