export type PeerRole = 'host' | 'participant'

export interface LivePeer {
  socketId: string
  participantId: string
  identitySub: string
  name: string
  role: PeerRole
  audio: boolean
  video: boolean
  screen: boolean
}

export interface WaitingPeer {
  socketId: string
  participantId: string
  identitySub: string
  name: string
}

const rooms = new Map<string, Map<string, LivePeer>>()
const lobbies = new Map<string, Map<string, WaitingPeer>>()

const bucket = (store: Map<string, Map<string, LivePeer | WaitingPeer>>, code: string) => {
  let room = store.get(code)
  if (!room) {
    room = new Map()
    store.set(code, room)
  }
  return room
}

export const publicPeer = (peer: LivePeer) => ({
  socketId: peer.socketId,
  participantId: peer.participantId,
  name: peer.name,
  role: peer.role,
  audio: peer.audio,
  video: peer.video,
  screen: peer.screen,
})

export const roomPeers = (code: string) => {
  const room = rooms.get(code)
  return room ? [...room.values()] : []
}

export const addPeer = (code: string, peer: LivePeer) => {
  bucket(rooms, code).set(peer.socketId, peer)
}

export const updatePeer = (code: string, socketId: string, patch: Partial<LivePeer>) => {
  const peer = rooms.get(code)?.get(socketId)
  if (!peer) return null
  Object.assign(peer, patch)
  return peer
}

export const removePeer = (code: string, socketId: string) => {
  const room = rooms.get(code)
  const peer = room?.get(socketId)
  room?.delete(socketId)
  if (room && room.size === 0) rooms.delete(code)
  return peer ?? null
}

export const peerBySocket = (socketId: string) => {
  for (const [code, room] of rooms) {
    const peer = room.get(socketId)
    if (peer) return { code, peer }
  }
  return null
}

export const peerByParticipant = (code: string, participantId: string) =>
  roomPeers(code).find((peer) => peer.participantId === participantId) ?? null

export const addWaiting = (code: string, peer: WaitingPeer) => {
  const lobby = lobbies.get(code) ?? new Map<string, WaitingPeer>()
  lobby.set(peer.socketId, peer)
  lobbies.set(code, lobby)
}

export const waitingPeers = (code: string) => [...(lobbies.get(code)?.values() ?? [])]

export const takeWaiting = (code: string, participantId: string) => {
  const lobby = lobbies.get(code)
  if (!lobby) return null
  for (const peer of lobby.values()) {
    if (peer.participantId === participantId) {
      lobby.delete(peer.socketId)
      if (lobby.size === 0) lobbies.delete(code)
      return peer
    }
  }
  return null
}

export const removeWaitingSocket = (socketId: string) => {
  for (const [code, lobby] of lobbies) {
    if (lobby.delete(socketId)) {
      if (lobby.size === 0) lobbies.delete(code)
      return code
    }
  }
  return null
}
