import os from 'node:os'
import mediasoup from 'mediasoup'
import { acquire } from './asyncLock.js'
import { MEDIASOUP_RTC_MIN_PORT, MEDIASOUP_RTC_MAX_PORT } from './constants.js'

let worker = null
const routers = new Map()
const transports = new Map()
export const producers = new Map()

const MEDIA_CODECS = [
  { kind: 'video', mimeType: 'video/H264', clockRate: 90000, parameters: { 'packetization-mode': 1, 'profile-level-id': '42e01f' } },
  { kind: 'video', mimeType: 'video/VP8', clockRate: 90000 },
  { kind: 'video', mimeType: 'video/VP9', clockRate: 90000 },
]

export const getWorker = async () => {
  if (worker) return worker
  worker = await mediasoup.createWorker({
    logLevel: process.env.NODE_ENV === 'development' ? 'debug' : 'warn',
    logTags: ['info', 'ice', 'dtls', 'rtp', 'srtp', 'rtcp'],
    rtcMinPort: MEDIASOUP_RTC_MIN_PORT,
    rtcMaxPort: MEDIASOUP_RTC_MAX_PORT,
  })
  worker.on('died', async (error) => {
    console.error('[mediasoup] Worker died:', error?.message || String(error))
    const { graceful } = await import('./shutdown.js')
    await graceful(error || new Error('Mediasoup worker died'))
  })
  return worker
}

export const getRouter = async (sessionId) => {
  return acquire(`router-${sessionId}`, async () => {
    const existing = routers.get(sessionId)
    if (existing) return existing
    const router = await (await getWorker()).createRouter({ mediaCodecs: MEDIA_CODECS })
    routers.set(sessionId, router)
    return router
  })
}

const isIPv4 = (host) => {
  if (typeof host !== 'string' || !host) return false
  const parts = host.split('.')
  if (parts.length !== 4) return false
  for (const p of parts) {
    const n = Number.parseInt(p, 10)
    if (Number.isNaN(n) || n < 0 || n > 255 || String(n) !== p) return false
  }
  return true
}

const getAnnouncedIpFromInterfaces = () => {
  for (const addrs of Object.values(os.networkInterfaces())) {
    if (!addrs) continue
    for (const addr of addrs) {
      if (addr.family === 'IPv4' && !addr.internal) return addr.address
    }
  }
  return null
}

const resolveAnnouncedIp = (requestHost) => {
  const envIp = process.env.MEDIASOUP_ANNOUNCED_IP?.trim()
  if (envIp) return envIp
  if (requestHost && isIPv4(requestHost)) return requestHost
  return getAnnouncedIpFromInterfaces()
}

export const createTransport = async (router, requestHost = undefined) => {
  return acquire(`transport-${router.id}`, async () => {
    const announcedIp = resolveAnnouncedIp(requestHost)
    const listenIps = announcedIp
      ? [{ ip: '0.0.0.0', announcedIp }, { ip: '127.0.0.1' }]
      : [{ ip: '127.0.0.1' }]

    const transport = await router.createWebRtcTransport({
      listenIps,
      enableUdp: true,
      enableTcp: true,
      preferUdp: true,
      initialAvailableOutgoingBitrate: 1_000_000,
    }).catch((err) => {
      console.error('[mediasoup] Transport creation failed:', err)
      throw new Error(`Failed to create transport: ${err.message || String(err)}`)
    })

    transports.set(transport.id, transport)
    transport.on('close', () => transports.delete(transport.id))

    return {
      transport,
      params: {
        id: transport.id,
        iceParameters: transport.iceParameters,
        iceCandidates: transport.iceCandidates,
        dtlsParameters: transport.dtlsParameters,
      },
    }
  })
}

export const getTransport = transportId => transports.get(transportId)

export const createProducer = async (transport, track) => {
  const producer = await transport.produce({ track })
  producers.set(producer.id, producer)
  producer.on('close', () => producers.delete(producer.id))
  return producer
}

export const getProducer = producerId => producers.get(producerId)

export const getTransports = () => transports

export const createConsumer = async (transport, producer, rtpCapabilities) => {
  if (producer.closed) throw new Error('Producer is closed')
  if (producer.paused) await producer.resume()

  const consumer = await transport.consume({
    producerId: producer.id,
    rtpCapabilities,
    paused: false,
  })

  consumer.on('transportclose', () => {})
  consumer.on('producerclose', () => {})

  return {
    consumer,
    params: {
      id: consumer.id,
      producerId: consumer.producerId,
      kind: consumer.kind,
      rtpParameters: consumer.rtpParameters,
    },
  }
}

export const closeRouter = async (sessionId) => {
  const router = routers.get(sessionId)
  if (router) {
    router.close()
    routers.delete(sessionId)
  }
}

export const getActiveRouters = () => Array.from(routers.keys())
