// LiveKit transport for real cross-device walkie-talkie audio.
const RadioLiveKit = (() => {
  const {
    Room,
    RoomEvent,
    Track,
    createLocalAudioTrack,
  } = window.LivekitClient || {};

  let room = null;
  let localTrack = null;
  let currentRoomName = null;
  let remoteAudioRoot = null;
  let listeners = {};

  function emit(name, payload) {
    const fn = listeners[name];
    if (fn) fn(payload);
  }

  function roomNameForChannel(channel) {
    return `walkie-ch-${String(channel).padStart(2, '0')}`;
  }

  async function fetchToken({ channel, identity }) {
    const roomName = roomNameForChannel(channel);
    const res = await fetch('/api/livekit-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ room: roomName, identity, name: identity }),
    });
    if (!res.ok) throw new Error(`LiveKit token failed: ${res.status}`);
    return res.json();
  }

  function ensureAudioRoot() {
    if (remoteAudioRoot) return remoteAudioRoot;
    remoteAudioRoot = document.createElement('div');
    remoteAudioRoot.id = 'livekit-remote-audio';
    remoteAudioRoot.style.display = 'none';
    document.body.appendChild(remoteAudioRoot);
    return remoteAudioRoot;
  }

  function attachTrack(track, participant) {
    if (track.kind !== Track.Kind.Audio) return;
    const el = track.attach();
    el.dataset.participant = participant.identity;
    ensureAudioRoot().appendChild(el);
  }

  function detachTrack(track, participant) {
    track.detach().forEach((el) => el.remove());
    emit('rxEnd', { from: participant.identity });
  }

  function isAudioPublication(publication) {
    return publication?.kind === Track.Kind.Audio || publication?.track?.kind === Track.Kind.Audio;
  }

  async function join({ channel, identity }) {
    if (!Room || !createLocalAudioTrack) {
      throw new Error('LiveKit client SDK is not loaded');
    }

    const nextRoomName = roomNameForChannel(channel);
    if (room && currentRoomName === nextRoomName) return;
    await leave();

    const auth = await fetchToken({ channel, identity });
    room = new Room({
      adaptiveStream: false,
      dynacast: true,
      audioCaptureDefaults: {
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true,
      },
    });

    room
      .on(RoomEvent.TrackSubscribed, attachTrack)
      .on(RoomEvent.TrackUnsubscribed, detachTrack)
      .on(RoomEvent.TrackUnmuted, (publication, participant) => {
        if (isAudioPublication(publication) && !participant.isLocal) emit('rxStart', { from: participant.identity });
      })
      .on(RoomEvent.TrackMuted, (publication, participant) => {
        if (isAudioPublication(publication) && !participant.isLocal) emit('rxEnd', { from: participant.identity });
      })
      .on(RoomEvent.ParticipantConnected, (participant) => emit('peerJoin', { from: participant.identity }))
      .on(RoomEvent.ParticipantDisconnected, (participant) => emit('peerLeave', { from: participant.identity }))
      .on(RoomEvent.Disconnected, () => emit('disconnected'));

    await room.connect(auth.url, auth.token);
    currentRoomName = auth.room;

    localTrack = await createLocalAudioTrack({
      echoCancellation: true,
      noiseSuppression: true,
      autoGainControl: true,
    });
    await localTrack.mute();
    await room.localParticipant.publishTrack(localTrack, {
      name: 'ptt-mic',
      source: Track.Source.Microphone,
    });

    emit('connected', { room: currentRoomName });
  }

  async function leave() {
    if (localTrack) {
      try { await localTrack.mute(); } catch (e) {}
      try { localTrack.stop(); } catch (e) {}
      localTrack = null;
    }
    if (room) {
      room.disconnect();
      room = null;
    }
    currentRoomName = null;
    if (remoteAudioRoot) remoteAudioRoot.replaceChildren();
  }

  async function startPtt() {
    if (!localTrack) throw new Error('LiveKit microphone is not ready');
    await localTrack.unmute();
  }

  async function stopPtt() {
    if (localTrack) await localTrack.mute();
  }

  function on(name, fn) {
    listeners[name] = fn;
  }

  return { join, leave, startPtt, stopPtt, on, roomNameForChannel };
})();

window.RadioLiveKit = RadioLiveKit;
