import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import Redis from 'ioredis';
import { env } from '../../../config/env.js';
import { logger } from '../../../utils/logger';

/**
 * SSE Chat Stream Routes
 *
 * Provides real-time updates for video chat using Server-Sent Events (SSE)
 * and Redis pub/sub for broadcasting.
 */

// Redis client for pub/sub
let redisPubClient: Redis | null = null;

function getRedisClient(): Redis {
  if (!redisPubClient) {
    redisPubClient = new Redis(env.REDIS_URL);
  }
  return redisPubClient;
}

export async function chatStreamRoutes(fastify: FastifyInstance) {
  /**
   * GET /public/:id/stream
   * SSE endpoint for real-time chat updates
   */
  fastify.get(
    '/public/:id/chat-stream',
    async (
      request: FastifyRequest<{ Params: { id: string } }>,
      reply: FastifyReply
    ) => {
      const videoId = parseInt(request.params.id, 10);

      if (isNaN(videoId)) {
        return reply.code(400).send({ message: 'Invalid video ID' });
      }

      // Set SSE headers
      reply.raw.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'X-Accel-Buffering': 'no', // Disable nginx buffering
      });

      // Create dedicated Redis subscriber for this connection
      const subscriber = new Redis(env.REDIS_URL);

      const channel = `video:${videoId}:chat`;

      // Set up message handler
      subscriber.on('message', (receivedChannel, message) => {
        if (receivedChannel === channel) {
          // Send message to client via SSE
          reply.raw.write(`data: ${message}\n\n`);
        }
      });

      // Subscribe to channel
      await subscriber.subscribe(channel);

      // Send initial connection confirmation
      reply.raw.write(`data: ${JSON.stringify({ type: 'connected', videoId })}\n\n`);

      // Keep-alive ping every 30 seconds
      const pingInterval = setInterval(() => {
        reply.raw.write(': ping\n\n');
      }, 30000);

      // Cleanup on disconnect
      request.raw.on('close', async () => {
        clearInterval(pingInterval);
        await subscriber.unsubscribe(channel);
        subscriber.disconnect();
      });
    }
  );
}

/**
 * Broadcast a new comment to all connected clients for a video
 */
export function broadcastCommentToVideo(
  videoId: number,
  comment: any
): void {
  try {
    const redis = getRedisClient();
    const channel = `video:${videoId}:chat`;
    const message = JSON.stringify({
      type: 'new_comment',
      comment,
    });
    redis.publish(channel, message);
  } catch (error) {
    logger.error('Failed to broadcast comment:', error);
  }
}

/**
 * Broadcast a new reaction to all connected clients for a video
 */
export function broadcastReactionToVideo(
  videoId: number,
  reaction: any
): void {
  try {
    const redis = getRedisClient();
    const channel = `video:${videoId}:chat`;
    const message = JSON.stringify({
      type: 'new_reaction',
      reaction,
    });
    redis.publish(channel, message);
  } catch (error) {
    logger.error('Failed to broadcast reaction:', error);
  }
}

/**
 * Cleanup Redis connections on shutdown
 */
export function closeRedisConnections(): void {
  if (redisPubClient) {
    redisPubClient.disconnect();
    redisPubClient = null;
  }
}
