import type { Response } from 'express';
import { logger } from '../../utils/logger';

interface PollSSEClient {
  id: string;
  res: Response;
  connectedAt: Date;
}

/** Poll-level SSE manager keyed by slug (supports anonymous viewers) */
class PollSSEService {
  private clients = new Map<string, PollSSEClient[]>(); // pollSlug -> connections
  private heartbeatInterval: NodeJS.Timeout | null = null;
  private static MAX_CONNECTIONS_PER_POLL = 200;

  startHeartbeat() {
    if (this.heartbeatInterval) return;
    this.heartbeatInterval = setInterval(() => {
      let total = 0;
      for (const [, clients] of this.clients) {
        for (const client of clients) {
          try {
            client.res.write(': heartbeat\n\n');
            total++;
          } catch {
            this.removeClient(client.id);
          }
        }
      }
      if (total > 0) {
        logger.debug(`Poll SSE heartbeat sent to ${total} clients`);
      }
    }, 30_000);
  }

  stopHeartbeat() {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      this.heartbeatInterval = null;
    }
  }

  addClient(slug: string, res: Response): string | null {
    const existing = this.clients.get(slug) ?? [];
    if (existing.length >= PollSSEService.MAX_CONNECTIONS_PER_POLL) {
      return null; // Reject — too many connections for this poll
    }

    const id = `${slug}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
    const client: PollSSEClient = { id, res, connectedAt: new Date() };

    if (!this.clients.has(slug)) {
      this.clients.set(slug, []);
    }
    this.clients.get(slug)!.push(client);

    logger.debug(`Poll SSE client connected: ${id} (poll: ${slug})`);
    return id;
  }

  removeClient(connectionId: string) {
    for (const [slug, clients] of this.clients) {
      const idx = clients.findIndex((c) => c.id === connectionId);
      if (idx >= 0) {
        clients.splice(idx, 1);
        if (clients.length === 0) {
          this.clients.delete(slug);
        }
        logger.debug(`Poll SSE client disconnected: ${connectionId} (poll: ${slug})`);
        return;
      }
    }
  }

  /** Broadcast event to all viewers of a poll */
  broadcast(slug: string, event: string, data: unknown) {
    const clients = this.clients.get(slug);
    if (!clients || clients.length === 0) return;

    const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;

    for (const client of clients) {
      try {
        client.res.write(payload);
      } catch {
        this.removeClient(client.id);
      }
    }
  }

  getConnectionCount(slug?: string): number {
    if (slug) return this.clients.get(slug)?.length ?? 0;
    let count = 0;
    for (const clients of this.clients.values()) {
      count += clients.length;
    }
    return count;
  }

  closeAll() {
    this.stopHeartbeat();
    for (const [, clients] of this.clients) {
      for (const client of clients) {
        try { client.res.end(); } catch {}
      }
    }
    this.clients.clear();
    logger.info('Poll SSE: All connections closed');
  }
}

export const pollSseService = new PollSSEService();
