///|
pub(all) enum NativeLinkSessionKind {
  NativeSessionNpcap
  NativeSessionRawSocket
  NativeSessionUdp
} derive(Eq, Debug)

///|
pub fn NativeLinkSessionKind::label(self : NativeLinkSessionKind) -> String {
  match self {
    NativeSessionNpcap => "npcap"
    NativeSessionRawSocket => "raw-socket"
    NativeSessionUdp => "udp"
  }
}

///|
pub(all) struct NativeLinkSessionConfig {
  kind : NativeLinkSessionKind
  interface_name : String
  bind_ip : Bytes
  bind_port : Int
  remote_ip : Bytes
  remote_port : Int
  ethertype : Int
  promiscuous : Bool
  snaplen : Int
  open_timeout_ms : Int
  receive_timeout_ms : Int
  filter_expression : String
  allow_live_open : Bool
} derive(Eq, Debug)

///|
pub(all) struct NativeLinkSessionDescriptor {
  id : String
  kind : NativeLinkSessionKind
  driver_id : String
  c_abi_owner_ids : Array[String]
  native_ffi_file : String
  fallback_ffi_file : String
  supports_open : Bool
  supports_send : Bool
  supports_receive : Bool
  supports_close : Bool
  supports_filter : Bool
  supports_frame_pool_route : Bool
  manual_interface_required : Bool
  default_live_open_allowed : Bool
  local_loopback_supported : Bool
  fallback_mirror_available : Bool
  evidence_label : String
} derive(Eq, Debug)

///|
pub(all) struct NativeLinkSessionCatalogReport {
  descriptor_count : Int
  npcap_session_count : Int
  raw_socket_session_count : Int
  udp_session_count : Int
  open_count : Int
  send_count : Int
  receive_count : Int
  close_count : Int
  filter_count : Int
  frame_pool_route_count : Int
  manual_interface_required_count : Int
  default_live_open_allowed_count : Int
  local_loopback_count : Int
  fallback_mirror_count : Int
  missing_c_abi_owner_count : Int
} derive(Eq, Debug)

///|
pub(all) struct NativeLinkSessionFramePoolSmokeReport {
  frame_pool_available : Bool
  frame_pool_route_supported : Bool
  staged_len : Int
  read_len : Int
  payload_digest : Int
  slot_released : Bool
  free_count_after_release : Int
  zero_copy_live_evidence : Bool
  evidence_label : String
} derive(Eq, Debug)

///|
pub(all) struct NativeUdpLinkSessionSmokeReport {
  native_backend_present : Bool
  udp_session_supported : Bool
  loopback_session_passed : Bool
  sent_len : Int
  received_len : Int
  payload_digest : Int
  live_nic_evidence : Bool
  evidence_label : String
} derive(Eq, Debug)

///|
pub(all) struct NativeNpcapLinkSession {
  adapter : NativeNpcapLiveAdapter
} derive(Eq, Debug)

///|
pub(all) struct NativeRawSocketLinkSession {
  adapter : NativeRawSocketAdapter
} derive(Eq, Debug)

///|
pub(all) struct NativeUdpLinkSession {
  socket : NativeUdpSocket
  remote_ip : Bytes
  remote_port : Int
} derive(Eq, Debug)

///|
pub fn native_link_session_config(
  kind : NativeLinkSessionKind,
) -> NativeLinkSessionConfig {
  {
    kind,
    interface_name: "",
    bind_ip: native_loopback_ipv4(),
    bind_port: 0,
    remote_ip: native_loopback_ipv4(),
    remote_port: 0,
    ethertype: 0x88b5,
    promiscuous: true,
    snaplen: 65_535,
    open_timeout_ms: 50,
    receive_timeout_ms: 50,
    filter_expression: "",
    allow_live_open: false,
  }
}

///|
pub fn native_link_session_catalog() -> Array[NativeLinkSessionDescriptor] {
  [
    {
      id: "native.link-session.npcap",
      kind: NativeSessionNpcap,
      driver_id: "driver.native.npcap",
      c_abi_owner_ids: [
        "native.stub.npcap_windows_live", "native.stub.npcap_windows_live_send",
        "native.stub.npcap_windows_live_filter", "native.stub.npcap_windows_live_capture",
        "native.stub.npcap_unsupported",
      ],
      native_ffi_file: "ffi_npcap_native.mbt",
      fallback_ffi_file: "ffi_npcap_fallback.mbt",
      supports_open: true,
      supports_send: true,
      supports_receive: true,
      supports_close: true,
      supports_filter: true,
      supports_frame_pool_route: true,
      manual_interface_required: true,
      default_live_open_allowed: false,
      local_loopback_supported: false,
      fallback_mirror_available: true,
      evidence_label: "native.link-session.npcap",
    },
    {
      id: "native.link-session.raw-socket",
      kind: NativeSessionRawSocket,
      driver_id: "driver.native.raw_socket",
      c_abi_owner_ids: ["native.stub.raw_socket_linux"],
      native_ffi_file: "ffi_raw_socket_native.mbt",
      fallback_ffi_file: "ffi_raw_socket_fallback.mbt",
      supports_open: true,
      supports_send: true,
      supports_receive: true,
      supports_close: true,
      supports_filter: false,
      supports_frame_pool_route: true,
      manual_interface_required: true,
      default_live_open_allowed: false,
      local_loopback_supported: false,
      fallback_mirror_available: true,
      evidence_label: "native.link-session.raw-socket",
    },
    {
      id: "native.link-session.udp",
      kind: NativeSessionUdp,
      driver_id: "driver.native.udp_socket",
      c_abi_owner_ids: [
        "native.stub.udp_windows_adapter", "native.stub.udp_windows_send", "native.stub.udp_windows_receive",
        "native.stub.udp_posix_adapter", "native.stub.udp_posix_send", "native.stub.udp_posix_receive",
        "native.stub.udp_unsupported",
      ],
      native_ffi_file: "ffi_udp_native.mbt",
      fallback_ffi_file: "ffi_udp_fallback.mbt",
      supports_open: true,
      supports_send: true,
      supports_receive: true,
      supports_close: true,
      supports_filter: false,
      supports_frame_pool_route: true,
      manual_interface_required: false,
      default_live_open_allowed: true,
      local_loopback_supported: true,
      fallback_mirror_available: true,
      evidence_label: "native.link-session.udp",
    },
  ]
}

///|
pub fn NativeLinkSessionDescriptor::passes(
  self : NativeLinkSessionDescriptor,
) -> Bool {
  self.id != "" &&
  self.driver_id != "" &&
  self.c_abi_owner_ids.length() > 0 &&
  self.native_ffi_file != "" &&
  self.fallback_ffi_file != "" &&
  self.supports_open &&
  self.supports_send &&
  self.supports_receive &&
  self.supports_close &&
  self.supports_frame_pool_route &&
  self.fallback_mirror_available &&
  self.evidence_label.has_prefix("native.link-session.") &&
  self.default_live_open_allowed == self.local_loopback_supported
}

///|
pub fn NativeLinkSessionCatalogReport::passes(
  self : NativeLinkSessionCatalogReport,
) -> Bool {
  self.descriptor_count == 3 &&
  self.npcap_session_count == 1 &&
  self.raw_socket_session_count == 1 &&
  self.udp_session_count == 1 &&
  self.open_count == 3 &&
  self.send_count == 3 &&
  self.receive_count == 3 &&
  self.close_count == 3 &&
  self.filter_count == 1 &&
  self.frame_pool_route_count == 3 &&
  self.manual_interface_required_count == 2 &&
  self.default_live_open_allowed_count == 1 &&
  self.local_loopback_count == 1 &&
  self.fallback_mirror_count == 3 &&
  self.missing_c_abi_owner_count == 0
}

///|
pub fn native_link_session_catalog_report() -> NativeLinkSessionCatalogReport {
  let catalog = native_link_session_catalog()
  {
    descriptor_count: catalog.length(),
    npcap_session_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.kind == NativeSessionNpcap
    }),
    raw_socket_session_count: count_link_session_descriptors(catalog, fn(
      entry,
    ) {
      entry.kind == NativeSessionRawSocket
    }),
    udp_session_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.kind == NativeSessionUdp
    }),
    open_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.supports_open
    }),
    send_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.supports_send
    }),
    receive_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.supports_receive
    }),
    close_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.supports_close
    }),
    filter_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.supports_filter
    }),
    frame_pool_route_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.supports_frame_pool_route
    }),
    manual_interface_required_count: count_link_session_descriptors(catalog, fn(
      entry,
    ) {
      entry.manual_interface_required
    }),
    default_live_open_allowed_count: count_link_session_descriptors(catalog, fn(
      entry,
    ) {
      entry.default_live_open_allowed
    }),
    local_loopback_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.local_loopback_supported
    }),
    fallback_mirror_count: count_link_session_descriptors(catalog, fn(entry) {
      entry.fallback_mirror_available
    }),
    missing_c_abi_owner_count: count_missing_link_session_owners(catalog),
  }
}

///|
pub fn NativeNpcapLinkSession::open(
  config : NativeLinkSessionConfig,
) -> NativeNpcapLinkSession raise NativeLinkError {
  if config.kind != NativeSessionNpcap {
    raise link_session_error(
      "npcap_link_session_open", -92, "session kind mismatch",
    )
  }
  if config.interface_name == "" {
    raise link_session_error(
      "npcap_link_session_open", -91, "manual interface required",
    )
  }
  if !config.allow_live_open {
    raise link_session_error(
      "npcap_link_session_open", -90, "manual live open permission required",
    )
  }
  let adapter = NativeNpcapLiveAdapter::open(
    config.interface_name,
    snaplen=config.snaplen,
    promiscuous=config.promiscuous,
    timeout_ms=config.open_timeout_ms,
  )
  if config.filter_expression != "" {
    adapter.set_filter(config.filter_expression)
  }
  { adapter, }
}

///|
pub fn NativeNpcapLinkSession::send_frame(
  self : NativeNpcapLinkSession,
  frame : Bytes,
) -> Int raise NativeLinkError {
  self.adapter.send_packet(frame)
}

///|
pub fn NativeNpcapLinkSession::receive_frame(
  self : NativeNpcapLinkSession,
  timeout_ms? : Int = 50,
) -> Bytes raise NativeLinkError {
  self.adapter.capture_one(timeout_ms~)
}

///|
pub fn NativeNpcapLinkSession::close(
  self : NativeNpcapLinkSession,
) -> Unit raise NativeLinkError {
  self.adapter.close()
}

///|
pub fn NativeRawSocketLinkSession::open(
  config : NativeLinkSessionConfig,
) -> NativeRawSocketLinkSession raise NativeLinkError {
  if config.kind != NativeSessionRawSocket {
    raise link_session_error(
      "raw_socket_link_session_open", -92, "session kind mismatch",
    )
  }
  if config.interface_name == "" {
    raise link_session_error(
      "raw_socket_link_session_open", -91, "manual interface required",
    )
  }
  if !config.allow_live_open {
    raise link_session_error(
      "raw_socket_link_session_open", -90, "manual live open permission required",
    )
  }
  let adapter = NativeRawSocketAdapter::open(
    config.interface_name,
    ethertype=config.ethertype,
    promiscuous=config.promiscuous,
    timeout_ms=config.open_timeout_ms,
  )
  { adapter, }
}

///|
pub fn NativeRawSocketLinkSession::send_frame(
  self : NativeRawSocketLinkSession,
  frame : Bytes,
) -> Int raise NativeLinkError {
  self.adapter.send_packet(frame)
}

///|
pub fn NativeRawSocketLinkSession::receive_frame(
  self : NativeRawSocketLinkSession,
  timeout_ms? : Int = 50,
) -> Bytes raise NativeLinkError {
  self.adapter.capture_one(timeout_ms~)
}

///|
pub fn NativeRawSocketLinkSession::close(
  self : NativeRawSocketLinkSession,
) -> Unit raise NativeLinkError {
  self.adapter.close()
}

///|
pub fn NativeUdpLinkSession::open(
  config : NativeLinkSessionConfig,
) -> NativeUdpLinkSession raise NativeLinkError {
  if config.kind != NativeSessionUdp {
    raise link_session_error(
      "udp_link_session_open", -92, "session kind mismatch",
    )
  }
  let socket = NativeUdpSocket::open(config.bind_ip, bind_port=config.bind_port)
  { socket, remote_ip: config.remote_ip, remote_port: config.remote_port }
}

///|
pub fn NativeUdpLinkSession::bound_port(
  self : NativeUdpLinkSession,
) -> Int raise NativeLinkError {
  self.socket.bound_port()
}

///|
pub fn NativeUdpLinkSession::send_frame(
  self : NativeUdpLinkSession,
  frame : Bytes,
) -> Int raise NativeLinkError {
  self.socket.send_to(self.remote_ip, self.remote_port, frame)
}

///|
pub fn NativeUdpLinkSession::receive_frame(
  self : NativeUdpLinkSession,
  timeout_ms? : Int = 50,
) -> Bytes raise NativeLinkError {
  self.socket.receive(timeout_ms)
}

///|
pub fn NativeUdpLinkSession::close(
  self : NativeUdpLinkSession,
) -> Unit raise NativeLinkError {
  self.socket.close()
}

///|
pub fn NativeFramePool::stage_link_session_frame(
  self : NativeFramePool,
  frame : Bytes,
) -> NativeFrameRef raise NativeLinkError {
  let fref0 = self.acquire()
  let buffer = link_session_bytes_to_fixed_array(frame)
  let fref1 = self.write(fref0, 0, buffer, frame.length())
  self.mark_filled(fref1, fref1.length)
}

///|
pub fn NativeFramePool::read_link_session_frame(
  self : NativeFramePool,
  fref : NativeFrameRef,
) -> Bytes raise NativeLinkError {
  let length = self.slot_length(fref)
  let buffer : FixedArray[Byte] = FixedArray::make(length, b'\x00')
  let read = self.read_into(fref, 0, buffer, length)
  link_session_fixed_array_to_bytes(buffer, read)
}

///|
pub fn native_link_session_frame_pool_smoke() -> NativeLinkSessionFramePoolSmokeReport raise NativeLinkError {
  let pool = NativeFramePool::create(capacity=1, frame_size=64)
  let payload : Bytes = [0xde, 0xad, 0xbe, 0xef, 0x4c, 0x57, 0x61]
  let fref = pool.stage_link_session_frame(payload)
  let got = pool.read_link_session_frame(fref)
  pool.release(fref)
  let free_count = pool.free_count()
  pool.destroy()
  {
    frame_pool_available: true,
    frame_pool_route_supported: got == payload,
    staged_len: fref.length,
    read_len: got.length(),
    payload_digest: bytes_digest(got),
    slot_released: true,
    free_count_after_release: free_count,
    zero_copy_live_evidence: false,
    evidence_label: "native.link-session.frame-pool-smoke",
  }
}

///|
pub fn native_udp_link_session_loopback_smoke(
  timeout_ms? : Int = 1_000,
) -> NativeUdpLinkSessionSmokeReport raise NativeLinkError {
  let rx_config = native_link_session_config(NativeSessionUdp)
  let rx = NativeUdpLinkSession::open(rx_config)
  let port = rx.bound_port()
  let mut tx_config = native_link_session_config(NativeSessionUdp)
  tx_config = { ..tx_config, remote_port: port }
  let tx = NativeUdpLinkSession::open(tx_config)
  let payload : Bytes = [0x4c, 0x57, 0x2d, 0x53, 0x45, 0x53, 0x53]
  let sent = tx.send_frame(payload)
  let received = rx.receive_frame(timeout_ms~)
  tx.close()
  rx.close()
  {
    native_backend_present: native_stub_version() > 0,
    udp_session_supported: true,
    loopback_session_passed: sent == payload.length() && received == payload,
    sent_len: sent,
    received_len: received.length(),
    payload_digest: bytes_digest(received),
    live_nic_evidence: false,
    evidence_label: "native.link-session.udp-loopback-smoke",
  }
}

///|
pub fn NativeLinkSessionFramePoolSmokeReport::passes(
  self : NativeLinkSessionFramePoolSmokeReport,
) -> Bool {
  self.frame_pool_available &&
  self.frame_pool_route_supported &&
  self.staged_len == self.read_len &&
  self.staged_len > 0 &&
  self.payload_digest > 0 &&
  self.slot_released &&
  self.free_count_after_release == 1 &&
  !self.zero_copy_live_evidence &&
  self.evidence_label == "native.link-session.frame-pool-smoke"
}

///|
pub fn NativeUdpLinkSessionSmokeReport::passes(
  self : NativeUdpLinkSessionSmokeReport,
) -> Bool {
  self.native_backend_present &&
  self.udp_session_supported &&
  self.loopback_session_passed &&
  self.sent_len == self.received_len &&
  self.payload_digest > 0 &&
  !self.live_nic_evidence &&
  self.evidence_label == "native.link-session.udp-loopback-smoke"
}

///|
fn link_session_error(
  op : String,
  code : Int,
  message : String,
) -> NativeLinkError {
  NativeLinkError::SystemError(op, code, message)
}

///|
fn count_link_session_descriptors(
  descriptors : ArrayView[NativeLinkSessionDescriptor],
  predicate : (NativeLinkSessionDescriptor) -> Bool,
) -> Int {
  let mut count = 0
  for descriptor in descriptors {
    if predicate(descriptor) {
      count += 1
    }
  }
  count
}

///|
fn count_missing_link_session_owners(
  descriptors : ArrayView[NativeLinkSessionDescriptor],
) -> Int {
  let mut count = 0
  for descriptor in descriptors {
    for owner in descriptor.c_abi_owner_ids {
      if !link_session_contains_module(owner) {
        count += 1
      }
    }
  }
  count
}

///|
fn link_session_contains_module(id : String) -> Bool {
  for entry in native_stub_module_catalog() {
    if entry.id == id {
      return true
    }
  }
  false
}

///|
fn link_session_bytes_to_fixed_array(bytes : Bytes) -> FixedArray[Byte] {
  let out = FixedArray::make(bytes.length(), b'\x00')
  for i in 0.. Bytes {
  let out : Array[Byte] = []
  let limit = min_int(max_int(count, 0), bytes.length())
  for i in 0..