import { supabase } from '@/integrations/supabase/client';
import {
  generateRoomCode,
  joinSignalingChannel,
  sendSignal,
  ICE_SERVERS,
  type SignalMessage,
} from './signaling';
import type { RealtimeChannel } from '@supabase/supabase-js';

export interface BroadcasterHandle {
  code: string;
  stop: () => Promise<void>;
  onViewerCountChange: (cb: (n: number) => void) => () => void;
}

const MAX_VIEWERS = 5;

/**
 * Starts broadcasting a MediaStream under a fresh room code.
 * Returns a handle exposing the code and a stop() to teardown.
 */
export async function startBroadcast(stream: MediaStream): Promise<BroadcasterHandle> {
  const code = generateRoomCode();

  // Insert room
  const { error: insErr } = await supabase.from('share_rooms').insert({ code, is_active: true });
  if (insErr) throw new Error(`Falha ao criar sala: ${insErr.message}`);

  const peers = new Map<string, RTCPeerConnection>();
  const listeners = new Set<(n: number) => void>();
  const notify = () => listeners.forEach((cb) => cb(peers.size));

  let channel: RealtimeChannel | null = null;

  const handleMessage = async (msg: SignalMessage) => {
    if (msg.type === 'viewer-join') {
      if (peers.size >= MAX_VIEWERS) return;
      const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
      peers.set(msg.viewerId, pc);

      stream.getTracks().forEach((t) => pc.addTrack(t, stream));

      pc.onicecandidate = (ev) => {
        if (ev.candidate && channel) {
          sendSignal(channel, {
            type: 'ice',
            viewerId: msg.viewerId,
            from: 'broadcaster',
            candidate: ev.candidate.toJSON(),
          });
        }
      };

      pc.onconnectionstatechange = () => {
        if (['failed', 'closed', 'disconnected'].includes(pc.connectionState)) {
          peers.delete(msg.viewerId);
          pc.close();
          notify();
          updateViewerCount(code, peers.size);
        }
      };

      const offer = await pc.createOffer();
      await pc.setLocalDescription(offer);
      if (channel) {
        sendSignal(channel, { type: 'offer', viewerId: msg.viewerId, sdp: offer });
      }
      notify();
      updateViewerCount(code, peers.size);
    } else if (msg.type === 'answer') {
      const pc = peers.get(msg.viewerId);
      if (pc && pc.signalingState !== 'stable') {
        await pc.setRemoteDescription(msg.sdp);
      }
    } else if (msg.type === 'ice' && msg.from === 'viewer') {
      const pc = peers.get(msg.viewerId);
      if (pc) {
        try {
          await pc.addIceCandidate(msg.candidate);
        } catch (e) {
          console.warn('[broadcaster] addIceCandidate failed', e);
        }
      }
    } else if (msg.type === 'viewer-leave') {
      const pc = peers.get(msg.viewerId);
      if (pc) {
        pc.close();
        peers.delete(msg.viewerId);
        notify();
        updateViewerCount(code, peers.size);
      }
    }
  };

  channel = joinSignalingChannel(code, handleMessage);

  // Stop when the underlying stream ends
  stream.getTracks().forEach((t) => {
    t.addEventListener('ended', () => stop());
  });

  let stopped = false;
  const stop = async () => {
    if (stopped) return;
    stopped = true;
    peers.forEach((pc) => pc.close());
    peers.clear();
    notify();
    if (channel) await supabase.removeChannel(channel);
    await supabase.from('share_rooms').update({ is_active: false, viewer_count: 0 }).eq('code', code);
  };

  return {
    code,
    stop,
    onViewerCountChange: (cb) => {
      listeners.add(cb);
      cb(peers.size);
      return () => listeners.delete(cb);
    },
  };
}

async function updateViewerCount(code: string, n: number) {
  await supabase.from('share_rooms').update({ viewer_count: n }).eq('code', code);
}
