import type { Subprocess } from 'bun'
import { toSubdomain } from '@urls'
import { LOCAL_HOST } from '%config'
import { networkInterfaces } from 'os'
import { hostLog } from './tui'

const MAX_REPUBLISH_DELAY = 30_000
const REPUBLISH_BASE_DELAY = 1_000

const _killed = new Set<string>()
const _publishers = new Map<string, Subprocess>()
const _republishAttempts = new Map<string, number>()

const isEnabled = process.env.NODE_ENV === 'production' && process.platform === 'linux'

function getLocalIp(): string | null {
  const interfaces = networkInterfaces()
  for (const iface of Object.values(interfaces)) {
    if (!iface) continue
    for (const addr of iface) {
      if (addr.family === 'IPv4' && !addr.internal) {
        return addr.address
      }
    }
  }
  return null
}

export function cleanupStalePublishers() {
  if (!isEnabled) return

  try {
    const result = Bun.spawnSync(['pkill', '-f', `avahi-publish.*${LOCAL_HOST}`])
    if (result.exitCode === 0) {
      hostLog('mDNS: cleaned up stale avahi-publish processes')
    }
  } catch {}
}

export function publishApp(name: string) {
  if (!isEnabled) return
  if (_publishers.has(name)) return

  const ip = getLocalIp()
  if (!ip) {
    hostLog(`mDNS: no local IP found, skipping ${name}`)
    return
  }

  const host = `${toSubdomain(name)}.${LOCAL_HOST}`

  try {
    const proc = Bun.spawn(['avahi-publish', '-a', host, '-R', ip], {
      stdout: 'ignore',
      stderr: 'ignore',
    })

    _publishers.set(name, proc)
    _republishAttempts.delete(name)
    hostLog(`mDNS: published ${host} -> ${ip}`)

    proc.exited.then(() => {
      _publishers.delete(name)
      if (!_killed.delete(name)) republish(name)
    })
  } catch {
    hostLog(`mDNS: failed to publish ${host}`)
  }
}

export function unpublishApp(name: string) {
  if (!isEnabled) return

  _republishAttempts.delete(name)
  const proc = _publishers.get(name)
  if (!proc) return

  _killed.add(name)
  proc.kill()
  _publishers.delete(name)
  hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`)
}

export function unpublishAll() {
  if (!isEnabled) return

  _republishAttempts.clear()
  for (const [name, proc] of _publishers) {
    _killed.add(name)
    proc.kill()
    hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`)
  }
  _publishers.clear()
}

function republish(name: string) {
  const attempts = _republishAttempts.get(name) ?? 0
  const delay = Math.min(REPUBLISH_BASE_DELAY * 2 ** attempts, MAX_REPUBLISH_DELAY)
  _republishAttempts.set(name, attempts + 1)

  hostLog(`mDNS: ${toSubdomain(name)}.${LOCAL_HOST} exited unexpectedly, retrying in ${delay}ms`)
  setTimeout(() => publishApp(name), delay)
}
