import { Server as SocketIOServer, Socket } from 'socket.io';
import { PerformanceMonitor } from '../../utils/performance-monitor.js';
import { SecurityManager } from '../../utils/security-manager.js';
import { ErrorHandler } from '../../utils/error-handler.js';

export function setupWebSocketHandlers(
  io: SocketIOServer,
  performanceMonitor: PerformanceMonitor,
  securityManager: SecurityManager,
  errorHandler: ErrorHandler,
  agileManager?: any
): void {
  console.error('📡 Setting up WebSocket handlers for real-time updates');

  io.on('connection', (socket: Socket) => {
    console.error(`📡 Client connected: ${socket.id}`);

    // Send initial data on connection
    handleInitialDataRequest(socket, performanceMonitor, securityManager, errorHandler, agileManager);

    // Handle client requests for specific data
    setupDataRequestHandlers(socket, performanceMonitor, securityManager, errorHandler, agileManager);

    // Handle agile board interactions
    if (agileManager) {
      setupAgileInteractionHandlers(socket, agileManager, io);
    }

    socket.on('disconnect', (reason) => {
      console.error(`📡 Client disconnected: ${socket.id} (${reason})`);
    });

    // Handle client errors
    socket.on('error', (error) => {
      console.error(`📡 WebSocket error from ${socket.id}:`, error);
    });
  });
}

async function handleInitialDataRequest(
  socket: Socket,
  performanceMonitor: PerformanceMonitor,
  securityManager: SecurityManager,
  errorHandler: ErrorHandler,
  agileManager?: any
): Promise<void> {
  try {
    // Send initial performance data
    const performanceData = await performanceMonitor.getSystemMetrics();
    socket.emit('initial_performance', performanceData);

    // Send initial security status
    const securityStatus = await securityManager.getSecurityStatus();
    socket.emit('initial_security', securityStatus);

    // Send initial error summary
    const errorPatterns = await errorHandler.analyzeErrorPatterns({
      timeRange: '24h',
      minOccurrences: 1,
      groupBy: 'type'
    });
    socket.emit('initial_errors', errorPatterns);

    // Send initial agile data if available
    if (agileManager) {
      try {
        const sprints = await agileManager.getSprints?.() || [];
        const activeSprint = await agileManager.getActiveSprint?.();
        const stories = await agileManager.getAllStories?.() || [];
        socket.emit('initial_agile', { 
          sprints, 
          activeSprint,
          stories 
        });
      } catch (error) {
        console.error('📡 Error loading agile data:', error);
        socket.emit('initial_agile', { sprints: [], stories: [] });
      }
    }

    console.error(`📡 Initial data sent to client: ${socket.id}`);
  } catch (error) {
    console.error(`📡 Error sending initial data to ${socket.id}:`, error);
    socket.emit('error', { message: 'Failed to load initial data' });
  }
}

function setupDataRequestHandlers(
  socket: Socket,
  performanceMonitor: PerformanceMonitor,
  securityManager: SecurityManager,
  errorHandler: ErrorHandler,
  agileManager?: any
): void {

  // Performance data requests
  socket.on('request_performance_metrics', async (timeRange = '24h') => {
    try {
      const metrics = await performanceMonitor.getSystemMetrics();
      socket.emit('performance_metrics_response', {
        timeRange,
        metrics,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      socket.emit('error', { message: 'Failed to fetch performance metrics' });
    }
  });

  socket.on('request_tool_metrics', async (toolName: string) => {
    try {
      const metrics = await performanceMonitor.getToolMetrics(toolName);
      socket.emit('tool_metrics_response', {
        toolName,
        metrics,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      socket.emit('error', { message: `Failed to fetch metrics for tool: ${toolName}` });
    }
  });

  // Security data requests
  socket.on('request_security_events', async (filters: any = {}) => {
    try {
      const events = await securityManager.getSecurityEvents(filters);
      socket.emit('security_events_response', {
        events,
        filters,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      socket.emit('error', { message: 'Failed to fetch security events' });
    }
  });

  // Error analysis requests
  socket.on('request_error_timeline', async (timeRange = '24h') => {
    try {
      const timeline = await errorHandler.getErrorTimeline({ timeRange });
      socket.emit('error_timeline_response', {
        timeline,
        timeRange,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      socket.emit('error', { message: 'Failed to fetch error timeline' });
    }
  });

  socket.on('request_error_patterns', async (options: any = {}) => {
    try {
      const patterns = await errorHandler.analyzeErrorPatterns({
        timeRange: '24h',
        minOccurrences: 2,
        groupBy: 'type',
        ...options
      });
      socket.emit('error_patterns_response', {
        patterns,
        options,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      socket.emit('error', { message: 'Failed to analyze error patterns' });
    }
  });
}

function setupAgileInteractionHandlers(
  socket: Socket,
  agileManager: any,
  io: SocketIOServer
): void {
  // Story movement (drag and drop)
  socket.on('move_story', async (data: { storyId: string; fromColumn: string; toColumn: string; index: number }) => {
    try {
      const { storyId, fromColumn, toColumn, index } = data;
      
      // Update story in agile manager (if method exists)
      if (agileManager.moveStory) {
        await agileManager.moveStory(storyId, toColumn, index);
      }

      // Broadcast the move to all connected clients
      io.emit('story_moved', {
        storyId,
        fromColumn,
        toColumn,
        index,
        timestamp: new Date().toISOString(),
        movedBy: socket.id
      });

      console.error(`📋 Story ${storyId} moved from ${fromColumn} to ${toColumn}`);
    } catch (error) {
      console.error(`📋 Error moving story:`, error);
      socket.emit('error', { message: 'Failed to move story' });
    }
  });

  // Story updates
  socket.on('update_story', async (data: { storyId: string; updates: any }) => {
    try {
      const { storyId, updates } = data;
      
      if (agileManager.updateStory) {
        await agileManager.updateStory(storyId, updates);
      }

      // Broadcast the update to all connected clients
      io.emit('story_updated', {
        storyId,
        updates,
        timestamp: new Date().toISOString(),
        updatedBy: socket.id
      });

      console.error(`📋 Story ${storyId} updated`);
    } catch (error) {
      console.error(`📋 Error updating story:`, error);
      socket.emit('error', { message: 'Failed to update story' });
    }
  });

  // Sprint updates
  socket.on('update_sprint', async (data: { sprintId: string; updates: any }) => {
    try {
      const { sprintId, updates } = data;
      
      if (agileManager.updateSprint) {
        await agileManager.updateSprint(sprintId, updates);
      }

      // Broadcast the update to all connected clients
      io.emit('sprint_updated', {
        sprintId,
        updates,
        timestamp: new Date().toISOString(),
        updatedBy: socket.id
      });

      console.error(`📋 Sprint ${sprintId} updated`);
    } catch (error) {
      console.error(`📋 Error updating sprint:`, error);
      socket.emit('error', { message: 'Failed to update sprint' });
    }
  });

  // Request current sprint data
  socket.on('request_sprint_data', async (sprintId?: string) => {
    try {
      let sprintData;
      
      if (sprintId && agileManager.getSprint) {
        sprintData = await agileManager.getSprint(sprintId);
      } else if (agileManager.getCurrentSprints) {
        sprintData = await agileManager.getCurrentSprints();
      }

      socket.emit('sprint_data_response', {
        sprintData,
        sprintId,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      console.error(`📋 Error fetching sprint data:`, error);
      socket.emit('error', { message: 'Failed to fetch sprint data' });
    }
  });

  // Request stories for a sprint or board
  socket.on('request_stories', async (filters: any = {}) => {
    try {
      let stories;
      
      if (agileManager.getStories) {
        stories = await agileManager.getStories(filters);
      } else if (agileManager.getActiveStories) {
        stories = await agileManager.getActiveStories();
      }

      socket.emit('stories_response', {
        stories,
        filters,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      console.error(`📋 Error fetching stories:`, error);
      socket.emit('error', { message: 'Failed to fetch stories' });
    }
  });
}