import Peer from "./peer.js";
import * as Logger from "../utils/logger.js";

function uuid4() {
  var temp_url = URL.createObjectURL(new Blob());
  var uuid = temp_url.toString();
  URL.revokeObjectURL(temp_url);
  return uuid.split(/[:/]/g).pop().toLowerCase();
}

export class RenderStreaming {
  constructor(signaling, config) {
    this._peer = null;           // participant端：单一peer
    this._peers = new Map();     // host端：多peer Map (participantId → Peer)
    this._connectionId = null;
    this._participantId = null;   // 自己的participantId
    this._isHost = false;
    this.onConnect = function (connectionId, data) { Logger.log(`Connect peer on ${connectionId}.`); };
    this.onDisconnect = function (connectionId) { Logger.log(`Disconnect peer on ${connectionId}.`); };
    this.onGotOffer = function (connectionId) { Logger.log(`On got Offer on ${connectionId}.`); };
    this.onGotAnswer = function (connectionId) { Logger.log(`On got Answer on ${connectionId}.`); };
    this.onTrackEvent = function (data) { Logger.log(`OnTrack event peer with data:${data}`); };
    this.onAddChannel = function (data) { Logger.log(`onAddChannel event peer with data:${data}`); };
    this.onMessage = function (data) { Logger.log(`On message: ${data}`); };
    this.onParticipantLeft = function (participantId) { Logger.log(`Participant left: ${participantId}.`); };
    this.onParticipantJoined = function (participantId) { Logger.log(`Participant joined: ${participantId}.`); };
    this.onNewPeer = function (participantId) { Logger.log(`New peer created for ${participantId}.`); };
    this._config = config;
    this._signaling = signaling;
    this._signaling.addEventListener('connect', this._onConnect.bind(this));
    this._signaling.addEventListener('disconnect', this._onDisconnect.bind(this));
    this._signaling.addEventListener('offer', this._onOffer.bind(this));
    this._signaling.addEventListener('answer', this._onAnswer.bind(this));
    this._signaling.addEventListener('candidate', this._onIceCandidate.bind(this));
    this._signaling.addEventListener('on-message', this._onMessage.bind(this));
    this._signaling.addEventListener('participant-left', this._onParticipantLeft.bind(this));
    this._signaling.addEventListener('participant-joined', this._onParticipantJoined.bind(this));
  }

  async _onConnect(e) {
    const data = e.detail;
    if (this._connectionId == data.connectionId) {
      this._participantId = data.participantId;
      this._isHost = data.role === 'host';

      if (!this._isHost) {
        // participant端：立即创建单一peer并开始协商
        this._preparePeerConnection(this._connectionId, data.polite, null);
      }
      // host端：不在connect时创建peer，等participant加入后再创建

      this.onConnect(data.connectionId, data);
    }
  }

  async _onDisconnect(e) {
    const data = e.detail;
    if (this._connectionId == data.connectionId) {
      this.onDisconnect(data.connectionId);
      if (this._peer) {
        this._peer.close();
        this._peer = null;
      }
      // 关闭所有host端peers
      this._peers.forEach((peer, participantId) => {
        peer.close();
      });
      this._peers.clear();
    }
  }

  async _onOffer(e) {
    const offer = e.detail;
    const participantId = offer.participantId;

    if (this._isHost) {
      // host端：为该participant创建或复用peer
      // host端始终使用polite=false（impolite），确保perfect negotiation中host的offer优先
      let peer = this._peers.get(participantId);
      if (!peer || (peer.pc && peer.pc.iceConnectionState === 'disconnected')) {
        if (peer) peer.close();
        peer = this._preparePeerConnection(this._connectionId, false, participantId);
      }
      const desc = new RTCSessionDescription({ sdp: offer.sdp, type: "offer" });
      try {
        await peer.onGotDescription(this._connectionId, desc);
      } catch (error) {
        Logger.warn(`Error on GotDescription for participant ${participantId}: ${error}`);
      }
    } else {
      // participant端：使用单一peer，始终使用polite=true
      if (this._peer && this._peer.pc && this._peer.pc.iceConnectionState === 'disconnected') {
        this._peer.close();
        this._peer = null;
      }
      if (!this._peer) {
        this._preparePeerConnection(offer.connectionId, true, null);
      }
      const desc = new RTCSessionDescription({ sdp: offer.sdp, type: "offer" });
      try {
        await this._peer.onGotDescription(offer.connectionId, desc);
      } catch (error) {
        Logger.warn(`Error on GotDescription: ${error}`);
      }
    }
  }

  async _onAnswer(e) {
    const answer = e.detail;
    const participantId = answer.participantId;
    const desc = new RTCSessionDescription({ sdp: answer.sdp, type: "answer" });

    if (this._isHost && participantId) {
      // host端：路由到对应participant的peer
      const peer = this._peers.get(participantId);
      if (peer) {
        try {
          await peer.onGotDescription(this._connectionId, desc);
        } catch (error) {
          Logger.warn(`Error on GotDescription answer for ${participantId}: ${error}`);
        }
      }
    } else if (this._peer) {
      // participant端
      try {
        await this._peer.onGotDescription(answer.connectionId, desc);
      } catch (error) {
        Logger.warn(`Error on GotDescription answer: ${error}`);
      }
    }
  }

  async _onIceCandidate(e) {
    const candidate = e.detail;
    const participantId = candidate.participantId;
    const iceCandidate = new RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid, sdpMLineIndex: candidate.sdpMLineIndex });

    if (this._isHost && participantId) {
      // host端：路由到对应participant的peer
      const peer = this._peers.get(participantId);
      if (peer) {
        await peer.onGotCandidate(this._connectionId, iceCandidate);
      }
    } else if (this._peer) {
      // participant端
      await this._peer.onGotCandidate(candidate.connectionId, iceCandidate);
    }
  }

  async _onMessage(e) {
    const data = e.detail;
    this.onMessage(data);
  }

  async _onParticipantLeft(e) {
    const data = e.detail;
    const participantId = data.participantId;
    Logger.log(`Participant left: ${participantId}`);

    // 关闭该participant的peer
    if (this._peers.has(participantId)) {
      const peer = this._peers.get(participantId);
      peer.close();
      this._peers.delete(participantId);
    }

    this.onParticipantLeft(participantId);
  }

  async _onParticipantJoined(e) {
    const data = e.detail;
    const participantId = data.participantId;
    Logger.log(`Participant joined: ${participantId}`);

    // host端：不在此处创建peer，等待participant的offer到达后在_onOffer中创建
    // 这样避免host和participant同时发offer导致的glare冲突
    // _onOffer会在收到participant的offer时自动创建peer（如果不存在）

    this.onParticipantJoined(participantId);
  }

  async createConnection(connectionId) {
    this._connectionId = connectionId ? connectionId : uuid4();
    await this._signaling.createConnection(this._connectionId);
  }

  async deleteConnection() {
    await this._signaling.deleteConnection(this._connectionId);
  }

  _preparePeerConnection(connectionId, polite, participantId) {
    // host端多peer模式：participantId标识目标participant
    // participant端单peer模式：participantId为null

    const peer = new Peer(connectionId, polite, this._config);

    // 保存peer
    if (participantId) {
      if (this._peers.has(participantId)) {
        const oldPeer = this._peers.get(participantId);
        oldPeer.close();
      }
      this._peers.set(participantId, peer);
    } else {
      if (this._peer) {
        this._peer.close();
      }
      this._peer = peer;
    }

    // 事件处理：附加participantId用于路由
    peer.addEventListener('trackevent', (e) => {
      const data = e.detail;
      data.participantId = participantId;
      this.onTrackEvent(data);
    });

    peer.addEventListener('adddatachannel', (e) => {
      const data = e.detail;
      this.onAddChannel(data);
    });

    peer.addEventListener('ongotoffer', (e) => {
      const id = e.detail.connectionId;
      this.onGotOffer(id);
    });

    peer.addEventListener('ongotanswer', (e) => {
      const id = e.detail.connectionId;
      this.onGotAnswer(id);
    });

    peer.addEventListener('sendoffer', (e) => {
      const offer = e.detail;
      this._signaling.sendOffer(offer.connectionId, offer.sdp, participantId);
    });

    peer.addEventListener('sendanswer', (e) => {
      const answer = e.detail;
      this._signaling.sendAnswer(answer.connectionId, answer.sdp, participantId);
    });

    peer.addEventListener('sendcandidate', (e) => {
      const candidate = e.detail;
      this._signaling.sendCandidate(candidate.connectionId, candidate.candidate, candidate.sdpMid, candidate.sdpMLineIndex, participantId);
    });

    this.onNewPeer(participantId || connectionId);
    return peer;
  }

  async getStats(participantId) {
    if (this._isHost && participantId) {
      const peer = this._peers.get(participantId);
      return peer ? await peer.getStats(this._connectionId) : null;
    }
    return this._peer ? await this._peer.getStats(this._connectionId) : null;
  }

  createDataChannel(label, participantId) {
    if (this._isHost && participantId) {
      const peer = this._peers.get(participantId);
      return peer ? peer.createDataChannel(this._connectionId, label) : null;
    }
    return this._peer ? this._peer.createDataChannel(this._connectionId, label) : null;
  }

  addTrack(track, participantId) {
    if (this._isHost && participantId) {
      const peer = this._peers.get(participantId);
      return peer ? peer.addTrack(this._connectionId, track) : null;
    }
    return this._peer ? this._peer.addTrack(this._connectionId, track) : null;
  }

  addTransceiver(trackOrKind, init, participantId) {
    if (this._isHost && participantId) {
      const peer = this._peers.get(participantId);
      return peer ? peer.addTransceiver(this._connectionId, trackOrKind, init) : null;
    }
    return this._peer ? this._peer.addTransceiver(this._connectionId, trackOrKind, init) : null;
  }

  getTransceivers(participantId) {
    if (this._isHost && participantId) {
      const peer = this._peers.get(participantId);
      return peer ? peer.getTransceivers(this._connectionId) : null;
    }
    return this._peer ? this._peer.getTransceivers(this._connectionId) : null;
  }

  sendMessage(message) {
    if (this._signaling && this._connectionId) {
      this._signaling.sendMessage(this._connectionId, message);
    }
  }

  async start() {
    await this._signaling.start();
  }

  async stop() {
    if (this._peer) {
      this._peer.close();
      this._peer = null;
    }
    this._peers.forEach((peer) => {
      peer.close();
    });
    this._peers.clear();

    if (this._signaling) {
      await this._signaling.stop();
      this._signaling = null;
    }
  }
}
