import assert from "node:assert/strict"
import { parseFlowPacket, protoName, resetFlowTemplatesForTests, templateExporterCountForTests } from "./traffic-flow-parse.js"
import { allocateOverlayAddress, FLOW_TARGET_SRC_AUTO, usablePublicHost } from "./traffic-flow-overlay.js"

function netflowV5One(): Buffer {
  const buf = Buffer.alloc(24 + 48)
  buf.writeUInt16BE(5, 0)
  buf.writeUInt16BE(1, 2)
  buf[24] = 10; buf[25] = 1; buf[26] = 1; buf[27] = 8
  buf[28] = 8; buf[29] = 8; buf[30] = 8; buf[31] = 8
  buf.writeUInt16BE(1, 24 + 12)
  buf.writeUInt32BE(10, 24 + 16)
  buf.writeUInt32BE(1500, 24 + 20)
  buf.writeUInt16BE(443, 24 + 32)
  buf.writeUInt16BE(443, 24 + 34)
  buf.writeUInt8(6, 24 + 38)
  return buf
}

resetFlowTemplatesForTests()
const flows = parseFlowPacket(netflowV5One(), "10.255.254.5")
assert.equal(flows.length, 1)
assert.equal(flows[0]?.src, "10.1.1.8")
assert.equal(flows[0]?.dst, "8.8.8.8")
assert.equal(flows[0]?.proto, 6)
assert.equal(flows[0]?.bytes, 1500)
assert.equal(flows[0]?.inIface, "1")
assert.equal(protoName(6), "TCP")
assert.equal(parseFlowPacket(Buffer.from([0, 1]), "1.1.1.1").length, 0)

const taken = new Set(["10.255.254.2"])
assert.equal(allocateOverlayAddress("10.255.254.0/24", "10.255.254.1", 1, taken), "10.255.254.3")
assert.equal(allocateOverlayAddress("10.255.254.0/24", "10.255.254.1", 2, new Set()), "10.255.254.3")

assert.equal(usablePublicHost("localhost:8000"), "")
assert.equal(usablePublicHost("127.0.0.1"), "")
assert.equal(usablePublicHost("192.168.1.10"), "")
assert.equal(usablePublicHost("mm.example.com:443"), "mm.example.com")
assert.equal(usablePublicHost("203.0.113.10"), "203.0.113.10")
assert.equal(FLOW_TARGET_SRC_AUTO, "0.0.0.0")

resetFlowTemplatesForTests()
{
  const tpl = Buffer.alloc(16 + 16 + 20)
  tpl.writeUInt16BE(10, 0)
  tpl.writeUInt16BE(tpl.length, 2)
  tpl.writeUInt16BE(2, 16)
  tpl.writeUInt16BE(16, 18)
  tpl.writeUInt16BE(256, 20)
  tpl.writeUInt16BE(2, 22)
  tpl.writeUInt16BE(8, 24)
  tpl.writeUInt16BE(4, 26)
  tpl.writeUInt16BE(12, 28)
  tpl.writeUInt16BE(4, 30)
  const data = Buffer.alloc(16 + 12)
  data.writeUInt16BE(10, 0)
  data.writeUInt16BE(data.length, 2)
  data.writeUInt16BE(256, 16)
  data.writeUInt16BE(12, 18)
  data[20] = 10; data[21] = 1; data[22] = 1; data[23] = 8
  data[24] = 8; data[25] = 8; data[26] = 8; data[27] = 8
  const fromTpl = parseFlowPacket(tpl, "172.18.0.2")
  assert.equal(fromTpl.length, 0)
  const fromData = parseFlowPacket(data, "172.18.0.2")
  assert.equal(fromData.length, 1)
  assert.equal(fromData[0]?.src, "10.1.1.8")
  assert.equal(fromData[0]?.dst, "8.8.8.8")
}

resetFlowTemplatesForTests()
{
  const tpl = Buffer.alloc(16 + 24)
  tpl.writeUInt16BE(10, 0)
  tpl.writeUInt16BE(tpl.length, 2)
  tpl.writeUInt16BE(2, 16)
  tpl.writeUInt16BE(24, 18)
  tpl.writeUInt16BE(256, 20)
  tpl.writeUInt16BE(4, 22)
  tpl.writeUInt16BE(8, 24)
  tpl.writeUInt16BE(4, 26)
  tpl.writeUInt16BE(12, 28)
  tpl.writeUInt16BE(4, 30)
  tpl.writeUInt16BE(10, 32)
  tpl.writeUInt16BE(4, 34)
  tpl.writeUInt16BE(82, 36)
  tpl.writeUInt16BE(6, 38)
  const data = Buffer.alloc(16 + 22)
  data.writeUInt16BE(10, 0)
  data.writeUInt16BE(data.length, 2)
  data.writeUInt16BE(256, 16)
  data.writeUInt16BE(22, 18)
  data[20] = 10; data[21] = 1; data[22] = 1; data[23] = 8
  data[24] = 8; data[25] = 8; data[26] = 8; data[27] = 8
  data.writeUInt32BE(13, 28)
  data.write("ether1", 32)
  parseFlowPacket(tpl, "10.255.254.3")
  const named = parseFlowPacket(data, "10.255.254.3")
  assert.equal(named.length, 1)
  assert.equal(named[0]?.inIface, "13")
  assert.equal(named[0]?.src, "10.1.1.8")
}

resetFlowTemplatesForTests()
{
  const tpl = Buffer.alloc(16 + 20)
  tpl.writeUInt16BE(10, 0)
  tpl.writeUInt16BE(tpl.length, 2)
  tpl.writeUInt16BE(2, 16)
  tpl.writeUInt16BE(20, 18)
  tpl.writeUInt16BE(256, 20)
  tpl.writeUInt16BE(3, 22)
  tpl.writeUInt16BE(8, 24)
  tpl.writeUInt16BE(4, 26)
  tpl.writeUInt16BE(12, 28)
  tpl.writeUInt16BE(4, 30)
  tpl.writeUInt16BE(82, 32)
  tpl.writeUInt16BE(6, 34)
  const data = Buffer.alloc(16 + 18)
  data.writeUInt16BE(10, 0)
  data.writeUInt16BE(data.length, 2)
  data.writeUInt16BE(256, 16)
  data.writeUInt16BE(18, 18)
  data[20] = 10; data[21] = 1; data[22] = 1; data[23] = 8
  data[24] = 8; data[25] = 8; data[26] = 8; data[27] = 8
  data.write("ether1", 28)
  parseFlowPacket(tpl, "10.255.254.4")
  const namedOnly = parseFlowPacket(data, "10.255.254.4")
  assert.equal(namedOnly[0]?.inIface, "ether1")
}

resetFlowTemplatesForTests()
{
  const fieldSpecs: Array<[number, number]> = [
    [8, 4],
    [12, 4],
    [10, 4],
    [14, 4],
    [15, 4],
    [152, 8],
    [153, 8],
    [1, 4],
  ]
  const tplSetLen = 4 + 4 + fieldSpecs.length * 4
  const tpl = Buffer.alloc(16 + tplSetLen)
  tpl.writeUInt16BE(10, 0)
  tpl.writeUInt16BE(tpl.length, 2)
  tpl.writeUInt16BE(2, 16)
  tpl.writeUInt16BE(tplSetLen, 18)
  tpl.writeUInt16BE(256, 20)
  tpl.writeUInt16BE(fieldSpecs.length, 22)
  let off = 24
  for (const [type, len] of fieldSpecs) {
    tpl.writeUInt16BE(type, off)
    tpl.writeUInt16BE(len, off + 2)
    off += 4
  }
  const recLen = fieldSpecs.reduce((n, [, len]) => n + len, 0)
  const data = Buffer.alloc(16 + 4 + recLen)
  data.writeUInt16BE(10, 0)
  data.writeUInt16BE(data.length, 2)
  data.writeUInt16BE(256, 16)
  data.writeUInt16BE(4 + recLen, 18)
  let d = 20
  data[d] = 10; data[d + 1] = 100; data[d + 2] = 1; data[d + 3] = 17; d += 4
  data[d] = 173; data[d + 1] = 194; data[d + 2] = 160; data[d + 3] = 163; d += 4
  data.writeUInt32BE(13, d); d += 4
  data.writeUInt32BE(42, d); d += 4
  data[d] = 198; data[d + 1] = 51; data[d + 2] = 100; data[d + 3] = 1; d += 4
  data.writeBigUInt64BE(1_700_000_000_000n, d); d += 8
  data.writeBigUInt64BE(1_700_000_060_000n, d); d += 8
  data.writeUInt32BE(1500, d)
  parseFlowPacket(tpl, "10.255.254.5")
  const extra = parseFlowPacket(data, "10.255.254.5")
  assert.equal(extra.length, 1)
  assert.equal(extra[0]?.src, "10.100.1.17")
  assert.equal(extra[0]?.dst, "173.194.160.163")
  assert.equal(extra[0]?.inIface, "13")
  assert.equal(extra[0]?.outIface, "42")
  assert.equal(extra[0]?.nextHop, "198.51.100.1")
  assert.equal(extra[0]?.flowStartMs, 1_700_000_000_000)
  assert.equal(extra[0]?.flowEndMs, 1_700_000_060_000)
  assert.equal(extra[0]?.bytes, 1500)
}

resetFlowTemplatesForTests()
{
  const tpl = Buffer.alloc(16 + 16 + 20)
  tpl.writeUInt16BE(10, 0)
  tpl.writeUInt16BE(tpl.length, 2)
  tpl.writeUInt16BE(2, 16)
  tpl.writeUInt16BE(16, 18)
  tpl.writeUInt16BE(256, 20)
  tpl.writeUInt16BE(2, 22)
  tpl.writeUInt16BE(8, 24)
  tpl.writeUInt16BE(4, 26)
  tpl.writeUInt16BE(12, 28)
  tpl.writeUInt16BE(4, 30)
  for (let i = 0; i < 260; i++) {
    parseFlowPacket(tpl, `203.0.${Math.floor(i / 250)}.${i % 250}`)
  }
  assert.ok(templateExporterCountForTests() <= 256)
}

console.log("traffic-flow-parse.test.ts: ok")
