import type { types } from 'mediasoup'
import type { RemoteSocket, Socket, Namespace as SocketNamespace } from 'socket.io'
import type { User } from '../prisma/client'

export interface ChadClient {
  socketId: string
  userId: User['id']
  username: User['username']
  displayName: User['displayName']
  inputMuted: boolean
  outputMuted: boolean
}

export interface ProducerShort {
  producerId: types.Producer['id']
  kind: types.MediaKind
}

export interface ErrorCallbackResult {
  error: string
}

export interface SuccessCallbackResult {
  ok: true
}

export type EventCallback<T = SuccessCallbackResult> = (result: T | ErrorCallbackResult) => void

export interface ClientToServerEvents {
  join: (
    options: {
      rtpCapabilities: types.RtpCapabilities
    },
    cb: EventCallback<ChadClient[]>
  ) => void
  getRtpCapabilities: (
    cb: EventCallback<types.RtpCapabilities>
  ) => void
  createTransport: (
    options: {
      producing: boolean
      consuming: boolean
    },
    cb: EventCallback<Pick<types.WebRtcTransport, 'id' | 'iceParameters' | 'iceCandidates' | 'dtlsParameters'>>
  ) => void
  connectTransport: (
    options: {
      transportId: types.WebRtcTransport['id']
      dtlsParameters: types.WebRtcTransport['dtlsParameters']
    },
    cb: EventCallback
  ) => void
  produce: (
    options: {
      transportId: types.WebRtcTransport['id']
      kind: types.MediaKind
      rtpParameters: types.RtpParameters
      appData: { source: 'share' | string }
    },
    cb: EventCallback<{ id: types.Producer['id'] }>
  ) => void
  closeProducer: (
    options: {
      producerId: types.Producer['id']
    },
    cb: EventCallback
  ) => void
  pauseProducer: (
    options: {
      producerId: types.Producer['id']
    },
    cb: EventCallback
  ) => void
  resumeProducer: (
    options: {
      producerId: types.Producer['id']
    },
    cb: EventCallback
  ) => void
  pauseConsumer: (
    options: {
      consumerId: types.Consumer['id']
    },
    cb: EventCallback
  ) => void
  resumeConsumer: (
    options: {
      consumerId: types.Consumer['id']
    },
    cb: EventCallback
  ) => void
  updateClient: (
    options: Partial<Pick<ChadClient, 'inputMuted' | 'outputMuted'>>,
    cb: EventCallback<ChadClient>
  ) => void
}

export interface ServerToClientEvents {
  authenticated: () => void
  newPeer: (arg: ChadClient) => void
  producers: (arg: ProducerShort[]) => void
  newConsumer: (
    arg: {
      socketId: string
      producerId: types.Producer['id']
      id: types.Consumer['id']
      kind: types.MediaKind
      rtpParameters: types.RtpParameters
      type: types.ConsumerType
      appData: types.Producer['appData']
      producerPaused: types.Consumer['producerPaused']
    },
    cb: EventCallback
  ) => void
  peerClosed: (arg: string) => void
  consumerClosed: (arg: { consumerId: string }) => void
  consumerPaused: (arg: { consumerId: string }) => void
  consumerResumed: (arg: { consumerId: string }) => void
  consumerScore: (arg: { consumerId: string, score: types.ConsumerScore }) => void
  clientChanged: (clientId: ChadClient['socketId'], client: ChadClient) => void
  speakingPeers: (arg: { clientId: ChadClient['socketId'], volume: types.AudioLevelObserverVolume['volume'] }[]) => void
  activeSpeaker: (clientId?: ChadClient['socketId']) => void
}

export interface InterServerEvent {}

export interface SocketData {
  joined: boolean
  userId: User['id']
  username: User['username']
  displayName: User['displayName']
  inputMuted: boolean
  outputMuted: boolean
  rtpCapabilities: types.RtpCapabilities
  transports: Map<types.WebRtcTransport['id'], types.WebRtcTransport>
  producers: Map<types.Producer['id'], types.Producer>
  consumers: Map<types.Consumer['id'], types.Consumer>
}

export type SomeSocket = Socket<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData> | RemoteSocket<ServerToClientEvents, SocketData>

export type Namespace = SocketNamespace<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
