import { Page, WebSocket } from '@playwright/test';

export interface WSConnection {
  url: string;
  timestamp: number;
  isNativeClient: boolean;
  isServerConnection: boolean;
  messages: WSMessage[];
  closed: boolean;
  closedAt?: number;
}

export interface WSMessage {
  direction: 'sent' | 'received';
  payload: string;
  timestamp: number;
  size: number;
}

export class WebSocketMonitor {
  private connections: WSConnection[] = [];
  private activeConnections: Map<string, WSConnection> = new Map();

  /**
   * Start monitoring WebSocket connections on the given page
   */
  monitor(page: Page) {
    page.on('websocket', (ws: WebSocket) => {
      this.recordConnection(ws);
    });
  }

  /**
   * Record a WebSocket connection and its messages
   */
  private recordConnection(ws: WebSocket) {
    const url = ws.url();
    const timestamp = Date.now();

    const connection: WSConnection = {
      url: url,
      timestamp: timestamp,
      isNativeClient: url.includes('localhost:3060'),
      isServerConnection: url.includes('jamkazam.local:6767') || url.includes('jamkazam.com:6767'),
      messages: [],
      closed: false,
    };

    console.log(`[WebSocket Monitor] Connection opened: ${url}`);

    // Listen for sent messages
    ws.on('framesent', frame => {
      const payload = frame.payload.toString();
      const message: WSMessage = {
        direction: 'sent',
        payload: payload,
        timestamp: Date.now(),
        size: payload.length,
      };
      connection.messages.push(message);
    });

    // Listen for received messages
    ws.on('framereceived', frame => {
      const payload = frame.payload.toString();
      const message: WSMessage = {
        direction: 'received',
        payload: payload,
        timestamp: Date.now(),
        size: payload.length,
      };
      connection.messages.push(message);
    });

    // Listen for close
    ws.on('close', () => {
      connection.closed = true;
      connection.closedAt = Date.now();
      console.log(`[WebSocket Monitor] Connection closed: ${url}`);
    });

    this.connections.push(connection);
    this.activeConnections.set(url, connection);
  }

  /**
   * Get all connections
   */
  getConnections(): WSConnection[] {
    return this.connections;
  }

  /**
   * Get active (non-closed) connections
   */
  getActiveConnections(): WSConnection[] {
    return this.connections.filter(conn => !conn.closed);
  }

  /**
   * Get native client connection (ws://localhost:3060/)
   */
  getNativeClientConnection(): WSConnection | undefined {
    return this.connections.find(conn => conn.isNativeClient);
  }

  /**
   * Get server connection (ws://jamkazam.local:6767/websocket)
   */
  getServerConnection(): WSConnection | undefined {
    return this.connections.find(conn => conn.isServerConnection);
  }

  /**
   * Get connection count
   */
  getConnectionCount(): number {
    return this.connections.length;
  }

  /**
   * Get total message count across all connections
   */
  getTotalMessageCount(): number {
    return this.connections.reduce((sum, conn) => sum + conn.messages.length, 0);
  }

  /**
   * Get messages from a specific connection
   */
  getMessagesFromConnection(url: string): WSMessage[] {
    const conn = this.connections.find(c => c.url === url);
    return conn ? conn.messages : [];
  }

  /**
   * Verify dual WebSocket connections are established
   */
  verifyDualConnections(): {
    hasNativeClient: boolean;
    hasServerConnection: boolean;
    bothEstablished: boolean;
  } {
    const hasNativeClient = this.getNativeClientConnection() !== undefined;
    const hasServerConnection = this.getServerConnection() !== undefined;

    return {
      hasNativeClient,
      hasServerConnection,
      bothEstablished: hasNativeClient && hasServerConnection,
    };
  }

  /**
   * Reset the monitor
   */
  reset() {
    this.connections = [];
    this.activeConnections.clear();
  }

  /**
   * Export connections to JSON
   */
  toJSON(): string {
    return JSON.stringify(
      this.connections.map(conn => ({
        ...conn,
        // Truncate messages for readability
        messages: conn.messages.map(msg => ({
          ...msg,
          payload: msg.payload.length > 200 ? msg.payload.substring(0, 200) + '...' : msg.payload,
        })),
      })),
      null,
      2
    );
  }
}
