///|
pub(all) enum ThreadxHalHookKind {
  ThreadxHookClock
  ThreadxHookIrq
  ThreadxHookDma
  ThreadxHookMac
  ThreadxHookCanFd
  ThreadxHookUart
  ThreadxHookFramePool
} derive(Eq, Debug)

///|
pub fn ThreadxHalHookKind::label(self : ThreadxHalHookKind) -> String {
  match self {
    ThreadxHookClock => "clock"
    ThreadxHookIrq => "irq"
    ThreadxHookDma => "dma"
    ThreadxHookMac => "mac"
    ThreadxHookCanFd => "can-fd"
    ThreadxHookUart => "uart"
    ThreadxHookFramePool => "frame-pool"
  }
}

///|
pub(all) struct ThreadxHalHookDescriptor {
  id : String
  kind : ThreadxHalHookKind
  hal_capability_id : String
  bsp_hook_symbol : String
  requires_bsp : Bool
  requires_irq : Bool
  requires_dma : Bool
  link_port : Bool
  evidence_slot : String
  reference_path : String
} derive(Eq, Debug)

///|
pub fn ThreadxHalHookDescriptor::is_board_backed(
  self : ThreadxHalHookDescriptor,
) -> Bool {
  self.requires_bsp && self.bsp_hook_symbol != "" && self.evidence_slot != ""
}

///|
pub(all) struct ThreadxHalSkeletonDescriptor {
  id : String
  profile : @os.CapabilityProfile
  reactor_id : String
  board_family : String
  reference_project_path : String
  include_package_ids : Array[String]
  excluded_package_ids : Array[String]
  hook_descriptors : Array[ThreadxHalHookDescriptor]
  requires_bsp_profile : Bool
  requires_threadx_build : Bool
  defer_thread_restricted_calls : Bool
  moonbit_c_ffi_surface : Bool
  sim_default_enabled : Bool
  wasm_ui_default_enabled : Bool
  board_evidence_present : Bool
  threadx_build_evidence_present : Bool
} derive(Eq, Debug)

///|
pub fn ThreadxHalSkeletonDescriptor::hook_count(
  self : ThreadxHalSkeletonDescriptor,
) -> Int {
  self.hook_descriptors.length()
}

///|
pub fn ThreadxHalSkeletonDescriptor::has_hook_kind(
  self : ThreadxHalSkeletonDescriptor,
  kind : ThreadxHalHookKind,
) -> Bool {
  self.hook_descriptors.any(fn(hook) { hook.kind == kind })
}

///|
pub fn ThreadxHalSkeletonDescriptor::has_hal_capability(
  self : ThreadxHalSkeletonDescriptor,
  id : String,
) -> Bool {
  self.hook_descriptors.any(fn(hook) { hook.hal_capability_id == id })
}

///|
pub fn ThreadxHalSkeletonDescriptor::includes_package(
  self : ThreadxHalSkeletonDescriptor,
  id : String,
) -> Bool {
  self.include_package_ids.any(fn(item) { item == id })
}

///|
pub fn ThreadxHalSkeletonDescriptor::excludes_package(
  self : ThreadxHalSkeletonDescriptor,
  id : String,
) -> Bool {
  self.excluded_package_ids.any(fn(item) { item == id })
}

///|
pub fn ThreadxHalSkeletonDescriptor::missing_hal_reference_count(
  self : ThreadxHalSkeletonDescriptor,
  catalog : ArrayView[HalCapabilityDescriptor],
) -> Int {
  count_threadx_hooks(self.hook_descriptors, fn(hook) {
    !catalog.any(fn(capability) { capability.id == hook.hal_capability_id })
  })
}

///|
pub fn ThreadxHalSkeletonDescriptor::passes_static_boundary(
  self : ThreadxHalSkeletonDescriptor,
) -> Bool {
  self.profile == @os.ProfileRealEmbedded &&
  self.reactor_id == "reactor.threadx" &&
  self.hook_count() == 7 &&
  self.requires_bsp_profile &&
  self.requires_threadx_build &&
  self.defer_thread_restricted_calls &&
  !self.moonbit_c_ffi_surface &&
  !self.sim_default_enabled &&
  !self.wasm_ui_default_enabled &&
  !self.board_evidence_present &&
  !self.threadx_build_evidence_present &&
  self.includes_package("lockwire/core") &&
  self.includes_package("lockwire/clock") &&
  self.includes_package("lockwire/hal") &&
  self.excludes_package("lockwire/sim") &&
  self.excludes_package("lockwire/wasm") &&
  self.excludes_package("lockwire/driver/native") &&
  self.has_hook_kind(ThreadxHookClock) &&
  self.has_hook_kind(ThreadxHookIrq) &&
  self.has_hook_kind(ThreadxHookDma) &&
  self.has_hook_kind(ThreadxHookMac) &&
  self.has_hook_kind(ThreadxHookCanFd) &&
  self.has_hook_kind(ThreadxHookUart) &&
  self.has_hook_kind(ThreadxHookFramePool) &&
  self.hook_descriptors.all(fn(hook) { hook.is_board_backed() })
}

///|
pub(all) struct ThreadxHalSkeletonReport {
  descriptor_count : Int
  hook_count : Int
  bsp_hook_count : Int
  irq_hook_count : Int
  dma_hook_count : Int
  link_port_hook_count : Int
  missing_hal_reference_count : Int
  include_package_count : Int
  excluded_package_count : Int
  c_ffi_surface_present : Bool
  sim_default_enabled : Bool
  wasm_ui_default_enabled : Bool
  board_evidence_present : Bool
  threadx_build_evidence_present : Bool
  defer_thread_restricted_calls : Bool
} derive(Eq, Debug)

///|
pub fn ThreadxHalSkeletonReport::passes(
  self : ThreadxHalSkeletonReport,
) -> Bool {
  self.descriptor_count == 1 &&
  self.hook_count == 7 &&
  self.bsp_hook_count == self.hook_count &&
  self.irq_hook_count >= 4 &&
  self.dma_hook_count >= 3 &&
  self.link_port_hook_count >= 4 &&
  self.missing_hal_reference_count == 0 &&
  self.include_package_count == 4 &&
  self.excluded_package_count >= 5 &&
  !self.c_ffi_surface_present &&
  !self.sim_default_enabled &&
  !self.wasm_ui_default_enabled &&
  !self.board_evidence_present &&
  !self.threadx_build_evidence_present &&
  self.defer_thread_restricted_calls
}

///|
pub fn threadx_hal_skeleton_descriptor() -> ThreadxHalSkeletonDescriptor {
  {
    id: "hal.threadx.stm32.skeleton",
    profile: @os.ProfileRealEmbedded,
    reactor_id: "reactor.threadx",
    board_family: "stm32-threadx",
    reference_project_path: "local-reference:control_com_card/rs485_tcp",
    include_package_ids: [
      "lockwire/core", "lockwire/clock", "lockwire/hal", "lockwire/reactor/threadx",
    ],
    excluded_package_ids: [
      "lockwire/sim", "lockwire/wasm", "lockwire/driver/native", "lockwire/trace/pcapng",
      "protocol-stack-runtime",
    ],
    hook_descriptors: threadx_hal_hook_catalog(),
    requires_bsp_profile: true,
    requires_threadx_build: true,
    defer_thread_restricted_calls: true,
    moonbit_c_ffi_surface: false,
    sim_default_enabled: false,
    wasm_ui_default_enabled: false,
    board_evidence_present: false,
    threadx_build_evidence_present: false,
  }
}

///|
pub fn threadx_hal_hook_catalog() -> Array[ThreadxHalHookDescriptor] {
  [
    {
      id: "threadx.hook.clock",
      kind: ThreadxHookClock,
      hal_capability_id: "hal.clock.embedded",
      bsp_hook_symbol: "lockwire_threadx_clock_now",
      requires_bsp: true,
      requires_irq: false,
      requires_dma: false,
      link_port: false,
      evidence_slot: "board.clock.tick-or-cycle-counter",
      reference_path: "docs/lockwire-plan.md#embedded-native",
    },
    {
      id: "threadx.hook.irq",
      kind: ThreadxHookIrq,
      hal_capability_id: "hal.irq.embedded",
      bsp_hook_symbol: "lockwire_threadx_irq_bind",
      requires_bsp: true,
      requires_irq: true,
      requires_dma: false,
      link_port: false,
      evidence_slot: "board.irq.dispatch",
      reference_path: "local-reference:control_com_card/rs485_tcp/Src/stm32f4xx_it.c",
    },
    {
      id: "threadx.hook.dma",
      kind: ThreadxHookDma,
      hal_capability_id: "hal.dma.embedded",
      bsp_hook_symbol: "lockwire_threadx_dma_region",
      requires_bsp: true,
      requires_irq: true,
      requires_dma: true,
      link_port: false,
      evidence_slot: "board.dma.sram-placement",
      reference_path: "local-reference:control_com_card/rs485_tcp/AGENTS.md",
    },
    {
      id: "threadx.hook.mac",
      kind: ThreadxHookMac,
      hal_capability_id: "hal.mac.link_port.embedded",
      bsp_hook_symbol: "lockwire_threadx_mac_link_port",
      requires_bsp: true,
      requires_irq: true,
      requires_dma: true,
      link_port: true,
      evidence_slot: "board.mac.netxduo-link",
      reference_path: "local-reference:control_com_card/rs485_tcp/Src/app_netxduo.c",
    },
    {
      id: "threadx.hook.canfd",
      kind: ThreadxHookCanFd,
      hal_capability_id: "hal.canfd.link_port.embedded",
      bsp_hook_symbol: "lockwire_threadx_canfd_link_port",
      requires_bsp: true,
      requires_irq: true,
      requires_dma: false,
      link_port: true,
      evidence_slot: "board.canfd.link-port",
      reference_path: "docs/lockwire-plan.md#threadx-hal",
    },
    {
      id: "threadx.hook.uart",
      kind: ThreadxHookUart,
      hal_capability_id: "hal.uart.link_port.embedded",
      bsp_hook_symbol: "lockwire_threadx_uart_link_port",
      requires_bsp: true,
      requires_irq: true,
      requires_dma: false,
      link_port: true,
      evidence_slot: "board.uart.rs485-idle-frame",
      reference_path: "local-reference:control_com_card/rs485_tcp/Src/app_netxduo.c",
    },
    {
      id: "threadx.hook.frame_pool",
      kind: ThreadxHookFramePool,
      hal_capability_id: "hal.frame_pool.embedded",
      bsp_hook_symbol: "lockwire_threadx_frame_pool_region",
      requires_bsp: true,
      requires_irq: false,
      requires_dma: true,
      link_port: true,
      evidence_slot: "board.frame-pool.sram-or-dma-region",
      reference_path: "local-reference:control_com_card/rs485_tcp/AGENTS.md",
    },
  ]
}

///|
pub fn threadx_hal_skeleton_report() -> ThreadxHalSkeletonReport {
  let descriptor = threadx_hal_skeleton_descriptor()
  {
    descriptor_count: 1,
    hook_count: descriptor.hook_count(),
    bsp_hook_count: count_threadx_hooks(descriptor.hook_descriptors, fn(hook) {
      hook.requires_bsp
    }),
    irq_hook_count: count_threadx_hooks(descriptor.hook_descriptors, fn(hook) {
      hook.requires_irq
    }),
    dma_hook_count: count_threadx_hooks(descriptor.hook_descriptors, fn(hook) {
      hook.requires_dma
    }),
    link_port_hook_count: count_threadx_hooks(descriptor.hook_descriptors, fn(
      hook,
    ) {
      hook.link_port
    }),
    missing_hal_reference_count: descriptor.missing_hal_reference_count(
      hal_capability_catalog(),
    ),
    include_package_count: descriptor.include_package_ids.length(),
    excluded_package_count: descriptor.excluded_package_ids.length(),
    c_ffi_surface_present: descriptor.moonbit_c_ffi_surface,
    sim_default_enabled: descriptor.sim_default_enabled,
    wasm_ui_default_enabled: descriptor.wasm_ui_default_enabled,
    board_evidence_present: descriptor.board_evidence_present,
    threadx_build_evidence_present: descriptor.threadx_build_evidence_present,
    defer_thread_restricted_calls: descriptor.defer_thread_restricted_calls,
  }
}

///|
fn count_threadx_hooks(
  hooks : ArrayView[ThreadxHalHookDescriptor],
  predicate : (ThreadxHalHookDescriptor) -> Bool,
) -> Int {
  let mut count = 0
  for hook in hooks {
    if predicate(hook) {
      count += 1
    }
  }
  count
}