// Initiator flow: Join room; receive 'ready' event (this event is triggered by the socket server after a new recipient enters) -> createPeerConnection + createDataChannel -> createAndSendOffer
import BaseWebRTC, { WebRTCConfig } from "./webrtc_base";
import { postLogToBackend } from "@/app/config/api";
const developmentEnv = process.env.NODE_ENV; // Development environment

export default class WebRTC_Initiator extends BaseWebRTC {
  constructor(config: WebRTCConfig) {
    super(config);
    this.setupInitiatorSocketListeners();
  }

  private setupInitiatorSocketListeners() {
    this.socket.on("ready", ({ peerId }) => {
      // The peerId of the new recipient who entered the room
      this.handleReady({ peerId });
    });
    // Add listener for recipient's response
    this.socket.on("recipient-ready", ({ peerId }) => {
      if (developmentEnv === "development")
        postLogToBackend(
          `[Initiator] Received recipient-ready from: ${peerId}`
        );
      this.handleReady({ peerId });
    });
    // Add answer handler listener
    this.socket.on("answer", ({ answer, peerId, from }) => {
      this.handleAnswer({ answer, peerId, from });
    });
  }

  // The sender creates a connection upon receiving the recipient's join
  private async handleReady({ peerId }: { peerId: string }): Promise<void> {
    // Recipient peerId
    // this.log('log',`Received ready signal from peer ${peerId}`);
    if (developmentEnv === "development")
      postLogToBackend(`Received ready signal from peer ${peerId}`);
    await this.createPeerConnection(peerId);
    await this.createDataChannel(peerId);
    await this.createAndSendOffer(peerId);
  }
  private async handleAnswer({
    answer,
    peerId,
    from,
  }: {
    answer: RTCSessionDescriptionInit;
    peerId: string;
    from: string;
  }): Promise<void> {
    // this.log('log',`Handling answer from peer ${from}`);
    if (developmentEnv === "development")
      postLogToBackend(`Handling answer from peer ${from}`);
    const peerConnection = this.peerConnections.get(from);
    if (!peerConnection) {
      this.fireError(`No peer connection found for peer ${from}`, { from });
      return;
    }

    try {
      await peerConnection.setRemoteDescription(
        new RTCSessionDescription(answer)
      );
      // this.log('log', `Remote description set for peer ${from}`);

      // Process queued ICE candidates after setting the remote description
      await this.addQueuedIceCandidates(from);
    } catch (error) {
      this.fireError("Error handling answer", { error, from });
    }
  }
  protected async createDataChannel(peerId: string): Promise<void> {
    const peerConnection = this.peerConnections.get(peerId);
    if (!peerConnection) {
      this.fireError(`No peer connection found for peer ${peerId}`, { peerId });
      return;
    }
    try {
      const dataChannel = peerConnection.createDataChannel("dataChannel", {
        ordered: true,
      });

      dataChannel.bufferedAmountLowThreshold = 262144; // 256KB for others

      this.setupDataChannel(dataChannel, peerId);
      this.dataChannels.set(peerId, dataChannel);
    } catch (error) {
      postLogToBackend(
        `Error creating DataChannel - peer: ${peerId}, error: ${error}`
      );
      this.fireError(`Error creating data channel for peer ${peerId}`, {
        error,
        peerId,
      });
    }
  }
  // If it is the initiator, create and send an offer to the signaling server to negotiate a connection with the recipient.
  private async createAndSendOffer(peerId: string): Promise<void> {
    // this.log('log', `Creating and sending offer to ${peerId}`);
    if (developmentEnv === "development")
      postLogToBackend(`createAndSendOffer for peerId: ${peerId}`);
    const peerConnection = this.peerConnections.get(peerId);
    if (!peerConnection) {
      this.fireError(`No peer connection found for peer ${peerId}`, { peerId });
      return;
    }

    try {
      const offer = await peerConnection.createOffer();
      await peerConnection.setLocalDescription(offer);
      // this.log('log','createAndSendOffer',peerId,this.roomId,offer);
      this.socket.emit("offer", {
        roomId: this.roomId,
        peerId: peerId,
        offer: offer,
        from: this.socket.id,
      });
    } catch (error) {
      this.fireError("Error creating and sending offer", { error, peerId });
    }
  }
}
