import type { FlowMapHop } from "@mmapp/contracts/traffic-flow"
import { fmtRate } from "@/lib/fmt-rate"

export interface MatchedNetflowHop {
  bps: number
  bpsFwd: number
  bpsRev: number
  bytes: number
}

function ifaceNorm(s: string | undefined): string {
  return (s ?? "").trim().toLowerCase()
}

function pairKey(a: string, b: string): string {
  const x = String(a)
  const y = String(b)
  return x <= y ? `${x}\t${y}` : `${y}\t${x}`
}

function mergeDirected(hops: FlowMapHop[], mapFromId: string): MatchedNetflowHop {
  let bytes = 0
  let bpsFwd = 0
  let bpsRev = 0
  const from = String(mapFromId)
  for (const h of hops) {
    bytes += h.bytes
    if (h.fromId === from) {
      bpsFwd += h.bpsFwd
      bpsRev += h.bpsRev
    } else {
      bpsFwd += h.bpsRev
      bpsRev += h.bpsFwd
    }
  }
  return { bytes, bpsFwd, bpsRev, bps: bpsFwd + bpsRev }
}

export function hopHasRate(h: MatchedNetflowHop | undefined): h is MatchedNetflowHop {
  return h != null && Number.isFinite(h.bps) && h.bps > 0
}

export function formatNetflowRate(hop: MatchedNetflowHop): string {
  return fmtRate(hop.bps / 1_000_000)
}

export function formatNetflowDir(hop: MatchedNetflowHop): string {
  return `↓${fmtRate(hop.bpsFwd / 1_000_000)} ↑${fmtRate(hop.bpsRev / 1_000_000)}`
}

/** GRE: сначала имя интерфейса туннеля на любом конце, иначе пара узлов. */
export function matchNetflowForGreEdge(
  edge: {
    tunnel: { name: string }
    fromServer: { id: string }
    toServer: { id: string }
  },
  hops: FlowMapHop[],
): MatchedNetflowHop | undefined {
  const name = ifaceNorm(edge.tunnel.name)
  const fromId = String(edge.fromServer.id)
  const toId = String(edge.toServer.id)
  if (name) {
    const ifaceHits = hops.filter((h) =>
      h.kind === "iface"
      && ifaceNorm(h.iface) === name
      && (h.fromId === fromId || h.fromId === toId),
    )
    if (ifaceHits.length) return mergeDirected(ifaceHits, fromId)
    const greNamed = hops.filter((h) =>
      h.kind === "gre"
      && ifaceNorm(h.iface) === name
      && (h.fromId === fromId || h.fromId === toId || h.toId === fromId || h.toId === toId),
    )
    if (greNamed.length) return mergeDirected(greNamed, fromId)
  }
  const want = pairKey(fromId, toId)
  const pairHits = hops.filter((h) =>
    h.kind === "gre" && Boolean(h.toId) && pairKey(h.fromId, h.toId) === want,
  )
  if (pairHits.length) return mergeDirected(pairHits, fromId)
  return undefined
}

/** WAN-аплинк HR: kind wan, иначе iface с тем же именем на homeId. */
export function matchNetflowForWan(
  homeId: string,
  wanIface: string,
  hops: FlowMapHop[],
): MatchedNetflowHop | undefined {
  const id = String(homeId)
  const iface = ifaceNorm(wanIface)
  if (!iface) return undefined
  const wanHits = hops.filter((h) =>
    h.kind === "wan" && h.fromId === id && ifaceNorm(h.iface) === iface,
  )
  if (wanHits.length) return mergeDirected(wanHits, id)
  const ifaceHits = hops.filter((h) =>
    h.kind === "iface" && h.fromId === id && ifaceNorm(h.iface) === iface,
  )
  if (ifaceHits.length) return mergeDirected(ifaceHits, id)
  return undefined
}
