/**
 * WebRTC Connection Manager
 * Handles peer-to-peer connections for remote desktop streaming
 */

export interface RTCConfig {
  iceServers: RTCIceServer[]
}

export interface ConnectionEvents {
  onTrack?: (stream: MediaStream) => void
  onDataChannel?: (channel: RTCDataChannel) => void
  onConnectionStateChange?: (state: RTCPeerConnectionState) => void
  onIceCandidate?: (candidate: RTCIceCandidate | null) => void
  onError?: (error: Error) => void
}

const DEFAULT_RTC_CONFIG: RTCConfig = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'stun:stun1.l.google.com:19302' },
    { urls: 'stun:stun2.l.google.com:19302' },
  ],
}

export class WebRTCConnection {
  private peerConnection: RTCPeerConnection | null = null
  private dataChannel: RTCDataChannel | null = null
  private events: ConnectionEvents = {}

  constructor(config: RTCConfig = DEFAULT_RTC_CONFIG) {
    if (typeof window === 'undefined') return
    
    this.peerConnection = new RTCPeerConnection(config)
    this.setupEventHandlers()
  }

  private setupEventHandlers() {
    if (!this.peerConnection) return

    this.peerConnection.ontrack = (event) => {
      this.events.onTrack?.(event.streams[0])
    }

    this.peerConnection.ondatachannel = (event) => {
      this.dataChannel = event.channel
      this.setupDataChannel()
      this.events.onDataChannel?.(event.channel)
    }

    this.peerConnection.onconnectionstatechange = () => {
      if (this.peerConnection) {
        this.events.onConnectionStateChange?.(this.peerConnection.connectionState)
      }
    }

    this.peerConnection.onicecandidate = (event) => {
      this.events.onIceCandidate?.(event.candidate)
    }
  }

  private setupDataChannel() {
    if (!this.dataChannel) return

    this.dataChannel.onopen = () => {
      console.log('[WebRTC] Data channel opened')
    }

    this.dataChannel.onclose = () => {
      console.log('[WebRTC] Data channel closed')
    }

    this.dataChannel.onerror = (error) => {
      console.error('[WebRTC] Data channel error:', error)
    }
  }

  on<K extends keyof ConnectionEvents>(event: K, handler: ConnectionEvents[K]) {
    this.events[event] = handler
  }

  // Create offer (for viewer)
  async createOffer(): Promise<RTCSessionDescriptionInit> {
    if (!this.peerConnection) throw new Error('PeerConnection not initialized')

    // Create data channel for input events
    this.dataChannel = this.peerConnection.createDataChannel('input', {
      ordered: true,
    })
    this.setupDataChannel()

    const offer = await this.peerConnection.createOffer({
      offerToReceiveVideo: true,
      offerToReceiveAudio: true,
    })

    await this.peerConnection.setLocalDescription(offer)
    return offer
  }

  // Set remote answer (for viewer)
  async setRemoteAnswer(answer: RTCSessionDescriptionInit) {
    if (!this.peerConnection) throw new Error('PeerConnection not initialized')
    await this.peerConnection.setRemoteDescription(answer)
  }

  // Create answer (for host/agent)
  async createAnswer(offer: RTCSessionDescriptionInit): Promise<RTCSessionDescriptionInit> {
    if (!this.peerConnection) throw new Error('PeerConnection not initialized')

    await this.peerConnection.setRemoteDescription(offer)
    const answer = await this.peerConnection.createAnswer()
    await this.peerConnection.setLocalDescription(answer)
    return answer
  }

  // Add ICE candidate
  async addIceCandidate(candidate: RTCIceCandidateInit) {
    if (!this.peerConnection) throw new Error('PeerConnection not initialized')
    await this.peerConnection.addIceCandidate(candidate)
  }

  // Send input event via data channel
  sendInputEvent(event: InputEvent) {
    if (!this.dataChannel || this.dataChannel.readyState !== 'open') return
    this.dataChannel.send(JSON.stringify(event))
  }

  // Get connection state
  getConnectionState(): RTCPeerConnectionState | null {
    return this.peerConnection?.connectionState ?? null
  }

  // Close connection
  close() {
    this.dataChannel?.close()
    this.peerConnection?.close()
    this.dataChannel = null
    this.peerConnection = null
  }
}

// Input event types
export type InputEventType = 'mousemove' | 'mousedown' | 'mouseup' | 'keydown' | 'keyup' | 'scroll'

export interface InputEvent {
  type: InputEventType
  timestamp: number
  data: MouseEventData | KeyEventData | ScrollEventData
}

export interface MouseEventData {
  x: number
  y: number
  button?: number
}

export interface KeyEventData {
  key: string
  code: string
  altKey?: boolean
  ctrlKey?: boolean
  shiftKey?: boolean
  metaKey?: boolean
}

export interface ScrollEventData {
  deltaX: number
  deltaY: number
}

// Helper to create input events
export function createMouseEvent(type: 'mousemove' | 'mousedown' | 'mouseup', x: number, y: number, button?: number): InputEvent {
  return {
    type,
    timestamp: Date.now(),
    data: { x, y, button },
  }
}

export function createKeyEvent(type: 'keydown' | 'keyup', event: KeyboardEvent): InputEvent {
  return {
    type,
    timestamp: Date.now(),
    data: {
      key: event.key,
      code: event.code,
      altKey: event.altKey,
      ctrlKey: event.ctrlKey,
      shiftKey: event.shiftKey,
      metaKey: event.metaKey,
    },
  }
}

export function createScrollEvent(deltaX: number, deltaY: number): InputEvent {
  return {
    type: 'scroll',
    timestamp: Date.now(),
    data: { deltaX, deltaY },
  }
}
