/**
 * Module Event Bus Implementation
 *
 * Provides event-driven communication between the credential manager
 * and other system modules for loose coupling and real-time updates.
 */

import { EventEmitter } from 'events'
import { Platform, Account } from '@/types/credential'
import { AccountStatus } from './AccountStatus'

// ============================================================================
// Event Types and Interfaces
// ============================================================================

export enum CredentialEvent {
  // Account Events
  ACCOUNT_LOADED = 'account:loaded',
  ACCOUNT_UPDATED = 'account:updated',
  ACCOUNT_REMOVED = 'account:removed',
  ACCOUNT_HEALTH_CHANGED = 'account:health_changed',
  ACCOUNT_STATUS_CHANGED = 'account:status_changed',

  // Configuration Events
  CONFIG_LOADED = 'config:loaded',
  CONFIG_UPDATED = 'config:updated',
  CONFIG_ERROR = 'config:error',
  CONFIG_WATCH_STARTED = 'config:watch_started',
  CONFIG_WATCH_STOPPED = 'config:watch_stopped',

  // Signing Events
  SIGNING_SUCCESS = 'signing:success',
  SIGNING_FAILURE = 'signing:failure',
  SIGNING_PERFORMANCE_WARNING = 'signing:performance_warning',
  BATCH_SIGNING_COMPLETED = 'signing:batch_completed',

  // Pool Events
  POOL_ACCOUNT_SELECTED = 'pool:account_selected',
  POOL_FAILOVER_TRIGGERED = 'pool:failover_triggered',
  POOL_HEALTH_CHECK_COMPLETED = 'pool:health_check_completed',
  POOL_LOAD_BALANCE_UPDATED = 'pool:load_balance_updated',

  // Trading Integration Events
  TRADING_REQUEST_SIGNED = 'trading:request_signed',
  TRADING_ACCOUNT_RESERVED = 'trading:account_reserved',
  TRADING_ACCOUNT_RELEASED = 'trading:account_released',
  TRADING_DISTRIBUTION_UPDATED = 'trading:distribution_updated',

  // System Events
  SYSTEM_READY = 'system:ready',
  SYSTEM_SHUTDOWN = 'system:shutdown',
  SYSTEM_ERROR = 'system:error',
  PERFORMANCE_METRICS_UPDATED = 'system:metrics_updated'
}

export interface EventPayload {
  timestamp: Date
  source: string
  eventId: string
  metadata?: Record<string, any>
}

export interface AccountEventPayload extends EventPayload {
  accountId: string
  platform: Platform
  account?: Account
  previousState?: any
  newState?: any
}

export interface ConfigEventPayload extends EventPayload {
  configPath?: string
  accountCount?: number
  errors?: string[]
  loadTime?: number
}

export interface SigningEventPayload extends EventPayload {
  accountId: string
  platform: Platform
  duration: number
  algorithm: string
  success: boolean
  error?: string
  performanceData?: {
    signTime: number
    verifyTime?: number
    batchSize?: number
  }
}

export interface PoolEventPayload extends EventPayload {
  platform: Platform
  accountId?: string
  poolStatus?: any
  failoverReason?: string
  loadBalanceStrategy?: string
}

export interface TradingEventPayload extends EventPayload {
  operation: string
  platform: Platform
  accountId: string
  tradeId?: string
  parameters?: Record<string, any>
  result?: any
}

export interface SystemEventPayload extends EventPayload {
  component: string
  status?: string
  metrics?: Record<string, any>
  error?: Error
}

// ============================================================================
// Event Listener Types
// ============================================================================

export type EventListener<T extends EventPayload = EventPayload> = (payload: T) => void | Promise<void>

export interface EventSubscription {
  event: CredentialEvent
  listener: EventListener
  once?: boolean
  priority?: number
}

// ============================================================================
// Main Event Bus Implementation
// ============================================================================

export class ModuleEventBus extends EventEmitter {
  private static instance: ModuleEventBus
  private subscriptions: Map<string, EventSubscription[]> = new Map()
  private eventHistory: Array<{ event: CredentialEvent; payload: EventPayload; timestamp: Date }> = []
  private maxHistorySize = 1000
  private metricsCollectionEnabled = true

  constructor() {
    super()
    this.setMaxListeners(100) // Increase max listeners for complex systems
  }

  /**
   * Get singleton instance
   */
  static getInstance(): ModuleEventBus {
    if (!ModuleEventBus.instance) {
      ModuleEventBus.instance = new ModuleEventBus()
    }
    return ModuleEventBus.instance
  }

  // ============================================================================
  // Event Publishing
  // ============================================================================

  /**
   * Publish account-related event
   */
  publishAccountEvent(
    event: CredentialEvent,
    accountId: string,
    platform: Platform,
    data?: Partial<AccountEventPayload>
  ): void {
    const payload: AccountEventPayload = {
      timestamp: new Date(),
      source: 'credential-manager',
      eventId: this.generateEventId(),
      accountId,
      platform,
      ...data
    }

    this.publishEvent(event, payload)
  }

  /**
   * Publish configuration event
   */
  publishConfigEvent(event: CredentialEvent, data?: Partial<ConfigEventPayload>): void {
    const payload: ConfigEventPayload = {
      timestamp: new Date(),
      source: 'credential-manager',
      eventId: this.generateEventId(),
      ...data
    }

    this.publishEvent(event, payload)
  }

  /**
   * Publish signing event
   */
  publishSigningEvent(
    event: CredentialEvent,
    accountId: string,
    platform: Platform,
    data: Partial<SigningEventPayload>
  ): void {
    const payload: SigningEventPayload = {
      timestamp: new Date(),
      source: 'credential-manager',
      eventId: this.generateEventId(),
      accountId,
      platform,
      duration: 0,
      algorithm: 'unknown',
      success: false,
      ...data
    }

    this.publishEvent(event, payload)
  }

  /**
   * Publish pool event
   */
  publishPoolEvent(
    event: CredentialEvent,
    platform: Platform,
    data?: Partial<PoolEventPayload>
  ): void {
    const payload: PoolEventPayload = {
      timestamp: new Date(),
      source: 'hedging-pool',
      eventId: this.generateEventId(),
      platform,
      ...data
    }

    this.publishEvent(event, payload)
  }

  /**
   * Publish trading event
   */
  publishTradingEvent(
    event: CredentialEvent,
    operation: string,
    platform: Platform,
    accountId: string,
    data?: Partial<TradingEventPayload>
  ): void {
    const payload: TradingEventPayload = {
      timestamp: new Date(),
      source: 'trading-integration',
      eventId: this.generateEventId(),
      operation,
      platform,
      accountId,
      ...data
    }

    this.publishEvent(event, payload)
  }

  /**
   * Publish system event
   */
  publishSystemEvent(
    event: CredentialEvent,
    component: string,
    data?: Partial<SystemEventPayload>
  ): void {
    const payload: SystemEventPayload = {
      timestamp: new Date(),
      source: component,
      eventId: this.generateEventId(),
      component,
      ...data
    }

    this.publishEvent(event, payload)
  }

  // ============================================================================
  // Event Subscription
  // ============================================================================

  /**
   * Subscribe to events with typed payload
   */
  subscribe<T extends EventPayload>(
    event: CredentialEvent,
    listener: EventListener<T>,
    options?: { once?: boolean; priority?: number }
  ): () => void {
    const subscription: EventSubscription = {
      event,
      listener: listener as EventListener,
      once: options?.once,
      priority: options?.priority || 0
    }

    this.addSubscription(event, subscription)

    // Set up EventEmitter listener
    const emitterListener = (payload: T) => {
      try {
        listener(payload)
      } catch (error) {
        this.publishSystemEvent(CredentialEvent.SYSTEM_ERROR, 'event-bus', {
          error: error instanceof Error ? error : new Error('Event listener error'),
          metadata: { event, eventId: payload.eventId }
        })
      }
    }

    if (options?.once) {
      this.once(event, emitterListener)
    } else {
      this.on(event, emitterListener)
    }

    // Return unsubscribe function
    return () => {
      this.removeListener(event, emitterListener)
      this.removeSubscription(event, subscription)
    }
  }

  /**
   * Subscribe to multiple events
   */
  subscribeToEvents(subscriptions: Array<{
    event: CredentialEvent
    listener: EventListener
    options?: { once?: boolean; priority?: number }
  }>): () => void {
    const unsubscribeFunctions = subscriptions.map(({ event, listener, options }) =>
      this.subscribe(event, listener, options)
    )

    return () => {
      unsubscribeFunctions.forEach(unsubscribe => unsubscribe())
    }
  }

  /**
   * Subscribe to all events (useful for logging)
   */
  subscribeToAll(listener: EventListener): () => void {
    const eventTypes = Object.values(CredentialEvent)
    const unsubscribeFunctions = eventTypes.map(event =>
      this.subscribe(event, listener)
    )

    return () => {
      unsubscribeFunctions.forEach(unsubscribe => unsubscribe())
    }
  }

  // ============================================================================
  // Event History and Metrics
  // ============================================================================

  /**
   * Get event history
   */
  getEventHistory(filter?: {
    event?: CredentialEvent
    source?: string
    since?: Date
    limit?: number
  }): Array<{ event: CredentialEvent; payload: EventPayload; timestamp: Date }> {
    let history = [...this.eventHistory]

    if (filter) {
      if (filter.event) {
        history = history.filter(entry => entry.event === filter.event)
      }
      if (filter.source) {
        history = history.filter(entry => entry.payload.source === filter.source)
      }
      if (filter.since) {
        history = history.filter(entry => entry.timestamp >= filter.since!)
      }
      if (filter.limit) {
        history = history.slice(-filter.limit)
      }
    }

    return history
  }

  /**
   * Get event metrics
   */
  getEventMetrics(): {
    totalEvents: number
    eventsByType: Record<string, number>
    eventsBySource: Record<string, number>
    recentEvents: number
    averageEventsPerMinute: number
  } {
    const now = new Date()
    const oneMinuteAgo = new Date(now.getTime() - 60000)
    const oneHourAgo = new Date(now.getTime() - 3600000)

    const recentEvents = this.eventHistory.filter(entry => entry.timestamp >= oneMinuteAgo).length
    const hourlyEvents = this.eventHistory.filter(entry => entry.timestamp >= oneHourAgo).length

    const eventsByType: Record<string, number> = {}
    const eventsBySource: Record<string, number> = {}

    this.eventHistory.forEach(entry => {
      eventsByType[entry.event] = (eventsByType[entry.event] || 0) + 1
      eventsBySource[entry.payload.source] = (eventsBySource[entry.payload.source] || 0) + 1
    })

    return {
      totalEvents: this.eventHistory.length,
      eventsByType,
      eventsBySource,
      recentEvents,
      averageEventsPerMinute: hourlyEvents / 60
    }
  }

  /**
   * Clear event history
   */
  clearHistory(): void {
    this.eventHistory = []
  }

  // ============================================================================
  // Configuration
  // ============================================================================

  /**
   * Enable or disable metrics collection
   */
  setMetricsCollection(enabled: boolean): void {
    this.metricsCollectionEnabled = enabled
  }

  /**
   * Set maximum history size
   */
  setMaxHistorySize(size: number): void {
    this.maxHistorySize = size
    if (this.eventHistory.length > size) {
      this.eventHistory = this.eventHistory.slice(-size)
    }
  }

  // ============================================================================
  // Private Helper Methods
  // ============================================================================

  private publishEvent(event: CredentialEvent, payload: EventPayload): void {
    // Store in history if metrics collection is enabled
    if (this.metricsCollectionEnabled) {
      this.eventHistory.push({
        event,
        payload: { ...payload },
        timestamp: new Date()
      })

      // Trim history if it exceeds max size
      if (this.eventHistory.length > this.maxHistorySize) {
        this.eventHistory = this.eventHistory.slice(-this.maxHistorySize)
      }
    }

    // Emit the event
    this.emit(event, payload)
  }

  private addSubscription(event: CredentialEvent, subscription: EventSubscription): void {
    const eventSubscriptions = this.subscriptions.get(event) || []
    eventSubscriptions.push(subscription)

    // Sort by priority (higher priority first)
    eventSubscriptions.sort((a, b) => (b.priority || 0) - (a.priority || 0))

    this.subscriptions.set(event, eventSubscriptions)
  }

  private removeSubscription(event: CredentialEvent, subscription: EventSubscription): void {
    const eventSubscriptions = this.subscriptions.get(event) || []
    const index = eventSubscriptions.indexOf(subscription)
    if (index >= 0) {
      eventSubscriptions.splice(index, 1)
      this.subscriptions.set(event, eventSubscriptions)
    }
  }

  private generateEventId(): string {
    return `evt_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }
}

// ============================================================================
// Global Event Bus Instance
// ============================================================================

export const globalEventBus = ModuleEventBus.getInstance()

// ============================================================================
// Convenience Functions
// ============================================================================

/**
 * Publish account event to global bus
 */
export function publishAccountEvent(
  event: CredentialEvent,
  accountId: string,
  platform: Platform,
  data?: Partial<AccountEventPayload>
): void {
  globalEventBus.publishAccountEvent(event, accountId, platform, data)
}

/**
 * Subscribe to account events
 */
export function subscribeToAccountEvents(
  listener: EventListener<AccountEventPayload>
): () => void {
  const accountEvents = [
    CredentialEvent.ACCOUNT_LOADED,
    CredentialEvent.ACCOUNT_UPDATED,
    CredentialEvent.ACCOUNT_REMOVED,
    CredentialEvent.ACCOUNT_HEALTH_CHANGED,
    CredentialEvent.ACCOUNT_STATUS_CHANGED
  ]

  const unsubscribeFunctions = accountEvents.map(event =>
    globalEventBus.subscribe(event, listener)
  )

  return () => {
    unsubscribeFunctions.forEach(unsubscribe => unsubscribe())
  }
}

/**
 * Subscribe to signing events
 */
export function subscribeToSigningEvents(
  listener: EventListener<SigningEventPayload>
): () => void {
  const signingEvents = [
    CredentialEvent.SIGNING_SUCCESS,
    CredentialEvent.SIGNING_FAILURE,
    CredentialEvent.SIGNING_PERFORMANCE_WARNING,
    CredentialEvent.BATCH_SIGNING_COMPLETED
  ]

  const unsubscribeFunctions = signingEvents.map(event =>
    globalEventBus.subscribe(event, listener)
  )

  return () => {
    unsubscribeFunctions.forEach(unsubscribe => unsubscribe())
  }
}