import { Logger } from 'pino';
import { SessionManager, ExtensionConnection } from './session-manager.js';

export interface RoutingRule {
  sessionId?: string;
  userId?: string;
  priority: number;
  condition?: (connection: ExtensionConnection) => boolean;
}

export interface RouteResult {
  connection: ExtensionConnection;
  sessionId: string;
  userId: string;
  routingReason: string;
}

export class ConnectionRouter {
  private logger: Logger;
  private sessionManager: SessionManager;
  private routingRules: RoutingRule[] = [];

  constructor(logger: Logger, sessionManager: SessionManager) {
    this.logger = logger;
    this.sessionManager = sessionManager;

    // Set up default routing rules
    this.setupDefaultRoutingRules();
  }

  /**
   * Set up default routing rules
   */
  private setupDefaultRoutingRules(): void {
    // Rule 1: Route by exact session ID match (highest priority)
    this.addRoutingRule({
      priority: 100,
      condition: (connection: ExtensionConnection) => true, // Will be filtered by sessionId parameter
    });

    // Rule 2: Route by user ID (medium priority)
    this.addRoutingRule({
      priority: 50,
      condition: (connection: ExtensionConnection) => connection.isActive,
    });

    // Rule 3: Route to any active connection (lowest priority)
    this.addRoutingRule({
      priority: 10,
      condition: (connection: ExtensionConnection) => connection.isActive,
    });
  }

  /**
   * Add a custom routing rule
   */
  addRoutingRule(rule: RoutingRule): void {
    this.routingRules.push(rule);
    // Sort by priority (highest first)
    this.routingRules.sort((a, b) => b.priority - a.priority);
  }

  /**
   * Route a message to the appropriate Chrome extension connection
   */
  routeMessage(message: any, sessionId?: string, userId?: string): RouteResult {
    this.logger.info('Routing message:', {
      action: message.action,
      sessionId,
      userId,
      messageId: message.id,
    });

    // Try to route by session ID first
    if (sessionId) {
      const connection = this.sessionManager.getConnectionBySessionId(sessionId);
      if (connection && connection.isActive) {
        return {
          connection,
          sessionId: connection.sessionId,
          userId: connection.userId,
          routingReason: 'exact_session_match',
        };
      } else {
        this.logger.warn(`No active connection found for session: ${sessionId}`);
      }
    }

    // Try to route by user ID
    if (userId) {
      const connection = this.sessionManager.getConnectionByUserId(userId);
      if (connection && connection.isActive) {
        return {
          connection,
          sessionId: connection.sessionId,
          userId: connection.userId,
          routingReason: 'user_id_match',
        };
      } else {
        this.logger.warn(`No active connection found for user: ${userId}`);
      }
    }

    // Apply routing rules to find best connection
    const activeConnections = this.sessionManager.getAllActiveConnections();

    if (activeConnections.length === 0) {
      throw new Error('No active Chrome extension connections available');
    }

    // Apply routing rules in priority order
    for (const rule of this.routingRules) {
      const candidates = activeConnections.filter((conn) => {
        // Apply session/user filters if specified in rule
        if (rule.sessionId && conn.sessionId !== rule.sessionId) return false;
        if (rule.userId && conn.userId !== rule.userId) return false;

        // Apply custom condition
        if (rule.condition && !rule.condition(conn)) return false;

        return true;
      });

      if (candidates.length > 0) {
        // Use the first candidate (could implement load balancing here)
        const selectedConnection = candidates[0];

        return {
          connection: selectedConnection,
          sessionId: selectedConnection.sessionId,
          userId: selectedConnection.userId,
          routingReason: `rule_priority_${rule.priority}`,
        };
      }
    }

    // Fallback: use first available active connection
    const fallbackConnection = activeConnections[0];
    return {
      connection: fallbackConnection,
      sessionId: fallbackConnection.sessionId,
      userId: fallbackConnection.userId,
      routingReason: 'fallback_first_available',
    };
  }

  /**
   * Route a message with load balancing
   */
  routeMessageWithLoadBalancing(message: any, sessionId?: string, userId?: string): RouteResult {
    // For session-specific requests, use exact routing
    if (sessionId || userId) {
      return this.routeMessage(message, sessionId, userId);
    }

    // For general requests, implement round-robin load balancing
    const activeConnections = this.sessionManager.getAllActiveConnections();

    if (activeConnections.length === 0) {
      throw new Error('No active Chrome extension connections available');
    }

    // Simple round-robin based on message timestamp
    const index = Date.now() % activeConnections.length;
    const selectedConnection = activeConnections[index];

    return {
      connection: selectedConnection,
      sessionId: selectedConnection.sessionId,
      userId: selectedConnection.userId,
      routingReason: 'load_balanced_round_robin',
    };
  }

  /**
   * Get routing statistics
   */
  getRoutingStats(): any {
    const stats = this.sessionManager.getStats();
    return {
      ...stats,
      routingRules: this.routingRules.length,
      routingRulesPriorities: this.routingRules.map((rule) => rule.priority),
    };
  }

  /**
   * Route message to specific connection type
   */
  routeToConnectionType(
    message: any,
    connectionType: 'newest' | 'oldest' | 'most_active',
  ): RouteResult {
    const activeConnections = this.sessionManager.getAllActiveConnections();

    if (activeConnections.length === 0) {
      throw new Error('No active Chrome extension connections available');
    }

    let selectedConnection: ExtensionConnection;

    switch (connectionType) {
      case 'newest':
        selectedConnection = activeConnections.reduce((newest, current) =>
          current.connectedAt > newest.connectedAt ? current : newest,
        );
        break;

      case 'oldest':
        selectedConnection = activeConnections.reduce((oldest, current) =>
          current.connectedAt < oldest.connectedAt ? current : oldest,
        );
        break;

      case 'most_active':
        selectedConnection = activeConnections.reduce((mostActive, current) =>
          current.lastActivity > mostActive.lastActivity ? current : mostActive,
        );
        break;

      default:
        selectedConnection = activeConnections[0];
    }

    return {
      connection: selectedConnection,
      sessionId: selectedConnection.sessionId,
      userId: selectedConnection.userId,
      routingReason: `connection_type_${connectionType}`,
    };
  }

  /**
   * Check if a specific session can handle a message type
   */
  canSessionHandleMessage(sessionId: string, messageType: string): boolean {
    const connection = this.sessionManager.getConnectionBySessionId(sessionId);

    if (!connection || !connection.isActive) {
      return false;
    }

    // Check if connection has been active recently
    const timeSinceActivity = Date.now() - connection.lastActivity;
    const maxInactiveTime = 5 * 60 * 1000; // 5 minutes

    if (timeSinceActivity > maxInactiveTime) {
      this.logger.warn(`Session ${sessionId} has been inactive for ${timeSinceActivity}ms`);
      return false;
    }

    // Add message type specific checks here if needed
    // For now, assume all active connections can handle all message types
    return true;
  }

  /**
   * Get recommended session for a user
   */
  getRecommendedSessionForUser(userId: string): string | null {
    const connection = this.sessionManager.getConnectionByUserId(userId);
    return connection ? connection.sessionId : null;
  }

  /**
   * Cleanup inactive routing rules
   */
  cleanupRoutingRules(): void {
    // Remove rules that reference non-existent sessions
    const validSessionIds = new Set(
      this.sessionManager.getAllActiveConnections().map((conn) => conn.sessionId),
    );

    const initialRuleCount = this.routingRules.length;
    this.routingRules = this.routingRules.filter((rule) => {
      if (rule.sessionId && !validSessionIds.has(rule.sessionId)) {
        return false;
      }
      return true;
    });

    const removedRules = initialRuleCount - this.routingRules.length;
    if (removedRules > 0) {
      this.logger.info(`Cleaned up ${removedRules} invalid routing rules`);
    }
  }
}
