/**
 * WebRTC Signaling Server
 * Reuses WebSocket infrastructure for WebRTC signaling
 * Handles peer connection negotiation and ICE candidate exchange
 */

import { WebSocketServer } from 'ws';
import { Peer } from 'peerjs';

type SignalingMessage = {
  type: 'offer' | 'answer' | 'ice-candidate' | 'disconnect';
  payload: RTCSessionDescriptionInit | RTCIceCandidate | { peerId: string };
  targetPeerId: string;
};

interface PeerConnection {
  peer: Peer;
  connections: Map<string, any>;
  iceCandidates: Map<string, RTCIceCandidate[]>;
}

export class WebRTCSignalingServer {
  private wss: WebSocketServer;
  private peers: Map<string, PeerConnection> = new Map();
  private port: number;

  constructor(port: number) {
    this.port = port;
    this.wss = new WebSocketServer({ port });
    this.initialize();
  }

  private initialize(): void {
    console.log(`WebRTC Signaling Server starting on port ${this.port}`);

    this.wss.on('connection', (ws: any, req) => {
      const url = new URL(req.url || '', `http://${req.headers.host}`);
      const peerId = url.searchParams.get('peerId') || `peer-${Date.now()}-${Math.random()}`;
      
      console.log(`WebRTC peer connected: ${peerId}`);

      const peerConnection: PeerConnection = {
        peer: new Peer(peerId, {
          host: 'localhost',
          port: this.port,
          path: '/webrtc',
        }),
        connections: new Map(),
        iceCandidates: new Map(),
      };

      this.peers.set(peerId, peerConnection);

      // Handle incoming signaling messages
      ws.on('message', (data: Buffer) => {
        try {
          const message: SignalingMessage = JSON.parse(data.toString());
          this.handleSignalingMessage(peerId, message, ws);
        } catch (error) {
          console.error('Error parsing signaling message:', error);
          ws.send(JSON.stringify({ type: 'error', payload: { message: 'Invalid message format' } }));
        }
      });

      // Handle disconnection
      ws.on('close', () => {
        console.log(`WebRTC peer disconnected: ${peerId}`);
        this.cleanupPeer(peerId);
      });

      // Send confirmation
      ws.send(JSON.stringify({ 
        type: 'connected', 
        payload: { peerId } 
      }));
    });

    console.log(`WebRTC Signaling Server started on port ${this.port}`);
  }

  private handleSignalingMessage(
    sourcePeerId: string,
    message: SignalingMessage,
    ws: any
  ): void {
    const { type, payload, targetPeerId } = message;
    const sourceConnection = this.peers.get(sourcePeerId);

    if (!sourceConnection) {
      console.warn(`Source peer not found: ${sourcePeerId}`);
      return;
    }

    switch (type) {
      case 'offer':
        this.handleOffer(sourcePeerId, targetPeerId, payload as RTCSessionDescriptionInit, ws);
        break;

      case 'answer':
        this.handleAnswer(sourcePeerId, targetPeerId, payload as RTCSessionDescriptionInit);
        break;

      case 'ice-candidate':
        this.handleIceCandidate(sourcePeerId, targetPeerId, payload as RTCIceCandidate);
        break;

      case 'disconnect':
        this.disconnectPeer(sourcePeerId, targetPeerId);
        break;
    }
  }

  private async handleOffer(
    sourcePeerId: string,
    targetPeerId: string,
    offer: RTCSessionDescriptionInit,
    ws: any
  ): Promise<void> {
    console.log(`Offer received from ${sourcePeerId} to ${targetPeerId}`);

    const targetConnection = this.peers.get(targetPeerId);
    if (!targetConnection) {
      console.warn(`Target peer not found: ${targetPeerId}`);
      ws.send(JSON.stringify({
        type: 'error',
        payload: { message: `Target peer ${targetPeerId} not found` },
      }));
      return;
    }

    // Store the connection
    if (!targetConnection.connections.has(sourcePeerId)) {
      const conn = targetConnection.peer.call(sourcePeerId, new MediaStream());
      targetConnection.connections.set(sourcePeerId, conn);

      // Handle connection events
      conn.on('stream', (stream: MediaStream) => {
        console.log(`Media stream received: ${targetPeerId} from ${sourcePeerId}`);
      });

      conn.on('close', () => {
        console.log(`Connection closed: ${targetPeerId} <-> ${sourcePeerId}`);
      });

      conn.on('error', (error: Error) => {
        console.error(`Connection error: ${targetPeerId} <-> ${sourcePeerId}`, error);
      });

      // Send accumulated ICE candidates
      const accumulatedCandidates = targetConnection.iceCandidates.get(sourcePeerId) || [];
      accumulatedCandidates.forEach(candidate => {
        conn.dataChannel.send(JSON.stringify({
          type: 'ice-candidate',
          payload: candidate,
          targetPeerId,
        }));
      });
    }

    // Forward offer to target peer
    const targetConn = targetConnection.connections.get(sourcePeerId);
    if (targetConn) {
      (targetConn as any).dataChannel.send(JSON.stringify({
        type: 'offer',
        payload: offer,
        targetPeerId: targetPeerId,
      }));
    }
  }

  private handleAnswer(
    sourcePeerId: string,
    targetPeerId: string,
    answer: RTCSessionDescriptionInit
  ): void {
    console.log(`Answer received from ${sourcePeerId} to ${targetPeerId}`);

    const targetConnection = this.peers.get(targetPeerId);
    if (targetConnection?.connections.has(sourcePeerId)) {
      targetConnection.connections.get(sourcePeerId).send(JSON.stringify({
        type: 'answer',
        payload: answer,
        targetPeerId: targetPeerId,
      }));
    }
  }

  private handleIceCandidate(
    sourcePeerId: string,
    targetPeerId: string,
    candidate: RTCIceCandidate
  ): void {
    const targetConnection = this.peers.get(targetPeerId);
    
    if (!targetConnection) {
      return;
    }

    // Forward ICE candidate to target peer
    if (targetConnection.connections.has(sourcePeerId)) {
      const conn = targetConnection.connections.get(sourcePeerId);
      if (conn) {
        (conn as any).send(JSON.stringify({
          type: 'ice-candidate',
          payload: candidate,
          targetPeerId: targetPeerId,
        }));
      }
    }
  }

  private disconnectPeer(sourcePeerId: string, targetPeerId: string): void {
    const sourceConnection = this.peers.get(sourcePeerId);
    if (sourceConnection?.connections.has(targetPeerId)) {
      sourceConnection.connections.get(targetPeerId).close();
      sourceConnection.connections.delete(targetPeerId);
      console.log(`Connection closed: ${sourcePeerId} <-> ${targetPeerId}`);
    }
  }

  private cleanupPeer(peerId: string): void {
    const peerConnection = this.peers.get(peerId);
    if (peerConnection) {
      // Close all connections
      peerConnection.connections.forEach((conn, connectedPeerId) => {
        conn.close();
        console.log(`Cleaned up connection: ${peerId} <-> ${connectedPeerId}`);
      });

      // Destroy PeerJS instance
      peerConnection.peer.destroy();

      // Remove from registry
      this.peers.delete(peerId);
    }
  }

  getPeerCount(): number {
    return this.peers.size;
  }

  getPeers(): string[] {
    return Array.from(this.peers.keys());
  }

  getPeerConnections(peerId: string): string[] {
    const peerConnection = this.peers.get(peerId);
    if (!peerConnection) return [];
    return Array.from(peerConnection.connections.keys());
  }

  close(): void {
    this.peers.forEach((_, peerId) => this.cleanupPeer(peerId));
    this.wss.close();
    console.log('WebRTC Signaling Server closed');
  }
}

export default WebRTCSignalingServer;
