import type { FastifyInstance } from 'fastify'
import { networkInterfaces } from 'node:os'
import { execFile } from 'node:child_process'
import { promisify } from 'node:util'
import { z } from 'zod'
import { getConfig, setConfig, logEvent } from '../db/index.js'

const execFileAsync = promisify(execFile)

/** Parses "a.b.c.d/n" into a 32-bit base int + prefix length. */
function parseCidr(cidr: string): { base: number; prefix: number } | null {
  const match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/.exec(cidr.trim())
  if (!match) return null
  const octets = match.slice(1, 5).map(Number)
  const prefix = Number(match[5])
  if (octets.some((o) => o < 0 || o > 255) || prefix < 0 || prefix > 32) return null
  const base = (octets[0]! << 24) | (octets[1]! << 16) | (octets[2]! << 8) | octets[3]!
  return { base, prefix }
}

function ipToInt(ip: string): number | null {
  const parts = ip.split('.').map(Number)
  if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return null
  return (parts[0]! << 24) | (parts[1]! << 16) | (parts[2]! << 8) | parts[3]!
}

function ipInCidr(ip: string, cidr: { base: number; prefix: number }): boolean {
  const mask = cidr.prefix === 0 ? 0 : (-1 << (32 - cidr.prefix)) >>> 0
  const ipInt = ipToInt(ip)
  if (ipInt === null) return false
  return ((ipInt & mask) >>> 0) === ((cidr.base & mask) >>> 0)
}

/** Finds this host's own IPv4 address that falls within the given mesh CIDR, if any. */
function findHostIpInCidr(cidr: { base: number; prefix: number }): string | null {
  for (const addrs of Object.values(networkInterfaces())) {
    for (const addr of addrs ?? []) {
      if (addr.family === 'IPv4' && ipInCidr(addr.address, cidr)) return addr.address
    }
  }
  return null
}

/**
 * Some meshes are routed rather than locally-addressed — e.g. a VPC peered into a
 * NetBird mesh, where this host keeps its own 192.x address but can still reach mesh
 * peers (100.x) through routing. In that case there's no local mesh IP to find, so we
 * fall back to pinging an admin-supplied peer/gateway IP that's known to be on the mesh.
 */
async function canReachIp(ip: string): Promise<boolean> {
  try {
    await execFileAsync('ping', ['-c', '1', '-W', '2', ip])
    return true
  } catch {
    return false
  }
}

function meshStatusPayload() {
  const required = getConfig('mesh.required') === 'true'
  const cidr = getConfig('mesh.cidr') ?? null
  const verifiedAt = getConfig('mesh.verifiedAt')
  const verifiedVia = getConfig('mesh.verifiedVia') ?? null
  const overridden = getConfig('mesh.overrideUntil') === 'permanent'
  const parsed = cidr ? parseCidr(cidr) : null
  return {
    required,
    verified: !!verifiedAt,
    verifiedAt: verifiedAt ?? null,
    verifiedVia,
    overridden,
    cidr,
    hostMeshIp: parsed ? findHostIpInCidr(parsed) : null,
  }
}

const verifySchema = z.object({ cidr: z.string().min(1), testIp: z.string().min(1).optional() })

export async function systemRoutes(app: FastifyInstance) {
  app.get('/api/system/mesh-status', { onRequest: [app.authenticate] }, async () => {
    return meshStatusPayload()
  })

  // Verification is mesh-tech-agnostic: any overlay network (NetBird, WireGuard,
  // ZeroTier, Tailscale...) assigns this host an IP in its own range — checked first.
  // If this host doesn't hold a local mesh IP (e.g. a VPC routed into the mesh), fall
  // back to pinging an admin-supplied peer/gateway IP known to be on the mesh.
  app.post('/api/system/mesh/verify', { onRequest: [app.requireAdmin] }, async (req, reply) => {
    const parsed = verifySchema.safeParse(req.body)
    if (!parsed.success) {
      return reply.code(400).send({ error: parsed.error.issues[0]?.message ?? 'Invalid input' })
    }
    const cidr = parseCidr(parsed.data.cidr)
    if (!cidr) return reply.code(400).send({ error: 'Invalid CIDR — expected format like 100.64.0.0/10' })

    const hostMeshIp = findHostIpInCidr(cidr)
    setConfig('mesh.cidr', parsed.data.cidr)

    let ok = !!hostMeshIp
    let message = hostMeshIp ? `Host is on the mesh (${hostMeshIp})` : 'No host IP found in that range'
    let via: 'local-ip' | 'reachable' | null = hostMeshIp ? 'local-ip' : null

    if (!ok && parsed.data.testIp) {
      if (!ipInCidr(parsed.data.testIp, cidr)) {
        return reply.code(400).send({ error: 'Test IP is not inside the given CIDR' })
      }
      const reachable = await canReachIp(parsed.data.testIp)
      ok = reachable
      via = reachable ? 'reachable' : null
      message = reachable
        ? `Host can reach ${parsed.data.testIp} on the mesh`
        : `Host has no local mesh IP and could not reach ${parsed.data.testIp}`
    }

    if (ok) {
      setConfig('mesh.verifiedAt', new Date().toISOString())
      setConfig('mesh.verifiedVia', via ?? 'local-ip')
      logEvent('mesh_verified', `Mesh verified (${via}) — ${message}`)
    } else {
      logEvent('mesh_verify_failed', `Mesh verification failed: ${message}`)
    }

    return { ok, message, hostMeshIp }
  })

  app.post('/api/system/mesh/override', { onRequest: [app.requireAdmin] }, async (req) => {
    setConfig('mesh.overrideUntil', 'permanent')
    logEvent('mesh_override', `Mesh gate skipped by admin (${(req.user as { username: string }).username})`)
    return meshStatusPayload()
  })

  app.put('/api/system/mesh/required', { onRequest: [app.requireAdmin] }, async (req) => {
    const { required } = req.body as { required: boolean }
    setConfig('mesh.required', required ? 'true' : 'false')
    logEvent('mesh_required_changed', `Mesh requirement set to ${required ? 'on' : 'off'}`)
    return meshStatusPayload()
  })
}
