import dns from 'dns/promises'

const raw = (process.env.OFFICE_IP_CHECK || 'disabled').trim()
const matchers = raw.split(',').map(s => s.trim()).filter(Boolean)

const TTL = 5 * 60 * 1000
const cache = new Map()

const PUBLIC_IP_URLS = [
  'https://api.ipify.org',
  'https://ifconfig.co/ip',
  'https://icanhazip.com',
]

function normalizeIP(ip) {
  return ip?.startsWith('::ffff:') ? ip.slice(7) : ip
}

function isIPv4(s) {
  return /^\d{1,3}(\.\d{1,3}){3}$/.test(s)
}

function ipInCidr(ip, cidr) {
  const [range, bits] = cidr.split('/')
  if (!isIPv4(ip) || !isIPv4(range)) return false
  const mask = ~(2 ** (32 - parseInt(bits)) - 1) >>> 0
  const toInt = s => s.split('.').reduce((a, o) => (a << 8) + parseInt(o), 0) >>> 0
  return (toInt(ip) & mask) === (toInt(range) & mask)
}

async function fetchPublicIP() {
  for (const url of PUBLIC_IP_URLS) {
    try {
      const ctrl = new AbortController()
      const timer = setTimeout(() => ctrl.abort(), 4000)
      const res = await fetch(url, { signal: ctrl.signal })
      clearTimeout(timer)
      if (!res.ok) continue
      const ip = (await res.text()).trim()
      if (isIPv4(ip)) return ip
    } catch {
      // try next
    }
  }
  return null
}

async function resolveDynamic(key, resolver) {
  const hit = cache.get(key)
  if (hit && Date.now() < hit.expiry) return hit.ip
  const ip = await resolver()
  if (ip) {
    cache.set(key, { ip, expiry: Date.now() + TTL })
    return ip
  }
  return hit ? hit.ip : null
}

export async function isOnsite(requestIP) {
  if (matchers.length === 0 || matchers.includes('disabled')) return true
  const ip = normalizeIP(requestIP)
  if (!ip) return false

  for (const m of matchers) {
    if (m === 'auto') {
      const pub = await resolveDynamic('auto', fetchPublicIP)
      if (pub && ip === pub) return true
    } else if (m.includes('/')) {
      if (ipInCidr(ip, m)) return true
    } else if (/[a-zA-Z]/.test(m)) {
      const resolved = await resolveDynamic(m, async () => {
        try { return (await dns.resolve4(m))[0] } catch { return null }
      })
      if (resolved && ip === resolved) return true
    } else {
      if (ip === m) return true
    }
  }
  return false
}
