///|
pub(all) struct NativeStubSourcePlatformGateRule {
  file : String
  required_gate : String
  forbidden_gate : String
  require_export : Bool
} derive(Eq, Debug)

///|
pub(all) struct NativeStubSourcePlatformGateFact {
  file : String
  required_gate_present : Bool
  forbidden_gate_present : Bool
  gate_closed : Bool
  export_present : Bool
  export_before_required_gate : Bool
} derive(Eq, Debug)

///|
pub(all) struct NativeStubSourcePlatformGatePolicyReport {
  rule_count : Int
  actual_file_count : Int
  missing_file_count : Int
  extra_file_count : Int
  required_gate_count : Int
  forbidden_gate_count : Int
  required_export_count : Int
  missing_required_gate_count : Int
  present_forbidden_gate_count : Int
  unclosed_gate_count : Int
  missing_required_export_count : Int
  export_before_gate_count : Int
  duplicate_gate_fact_count : Int
  replaces_file_reading : Bool
  replaces_gate_extraction : Bool
  live_io_evidence : Bool
} derive(Eq, Debug)

///|
pub fn NativeStubSourcePlatformGatePolicyReport::passes(
  self : NativeStubSourcePlatformGatePolicyReport,
) -> Bool {
  self.rule_count > 0 &&
  self.actual_file_count == self.rule_count &&
  self.missing_file_count == 0 &&
  self.extra_file_count == 0 &&
  self.required_gate_count == self.rule_count &&
  self.forbidden_gate_count == self.rule_count &&
  self.required_export_count > 0 &&
  self.missing_required_gate_count == 0 &&
  self.present_forbidden_gate_count == 0 &&
  self.unclosed_gate_count == 0 &&
  self.missing_required_export_count == 0 &&
  self.export_before_gate_count == 0 &&
  self.duplicate_gate_fact_count == 0 &&
  !self.replaces_file_reading &&
  !self.replaces_gate_extraction &&
  !self.live_io_evidence
}

///|
pub fn native_stub_source_platform_gate_rules() -> Array[
  NativeStubSourcePlatformGateRule,
] {
  let rules : Array[NativeStubSourcePlatformGateRule] = []
  push_source_platform_gate_rules(
    rules,
    ["stub_clock_windows.c"],
    "#if defined(_WIN32)",
    "#if !defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_clock_posix.c"],
    "#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)",
    "#if !defined(__linux__)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_clock_unsupported.c"],
    "#if !defined(_WIN32) && !defined(__linux__) && !defined(__APPLE__) && !defined(__FreeBSD__)",
    "#if defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    [
      "stub_npcap_windows_loader.c", "stub_npcap_windows_listing.c", "stub_npcap_windows_live.c",
      "stub_npcap_windows_live_send.c", "stub_npcap_windows_live_filter.c", "stub_npcap_windows_live_capture.c",
    ],
    "#if defined(_WIN32)",
    "#if !defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_npcap_windows_common.c"],
    "#if defined(_WIN32)",
    "#if !defined(_WIN32)",
    false,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_npcap_unsupported.c"],
    "#if !defined(_WIN32)",
    "#if defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    [
      "stub_raw_socket_linux_adapter.c", "stub_raw_socket_linux_send.c", "stub_raw_socket_linux_capture.c",
      "stub_raw_socket_linux_list.c",
    ],
    "#if defined(__linux__)",
    "#if !defined(__linux__)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_raw_socket_linux_common.c"],
    "#if defined(__linux__)",
    "#if !defined(__linux__)",
    false,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_raw_socket_unsupported.c"],
    "#if !defined(__linux__)",
    "#if defined(__linux__)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    [
      "stub_udp_windows_adapter.c", "stub_udp_windows_send.c", "stub_udp_windows_recv.c",
    ],
    "#if defined(_WIN32)",
    "#if !defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_udp_windows_common.c"],
    "#if defined(_WIN32)",
    "#if !defined(_WIN32)",
    false,
  )
  push_source_platform_gate_rules(
    rules,
    [
      "stub_udp_posix_adapter.c", "stub_udp_posix_send.c", "stub_udp_posix_recv.c",
    ],
    "#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)",
    "#if !defined(__linux__)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_udp_posix_common.c"],
    "#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)",
    "#if !defined(__linux__)",
    false,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_udp_unsupported.c"],
    "#if !defined(_WIN32)",
    "#if defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    [
      "stub_clock_millis_windows.c", "stub_sleep_windows.c", "stub_rt_affinity_windows.c",
      "stub_npcap_windows_local_mac.c", "stub_npcap_windows_nonblock.c",
    ],
    "#if defined(_WIN32)",
    "#if !defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_clock_millis_posix.c", "stub_sleep_posix.c"],
    "#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)",
    "#if !defined(__linux__)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_clock_millis_unsupported.c", "stub_sleep_unsupported.c"],
    "#if !defined(_WIN32) && !defined(__linux__) && !defined(__APPLE__) && !defined(__FreeBSD__)",
    "#if defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_rt_affinity_unsupported.c", "stub_npcap_unsupported_live_ext.c"],
    "#if !defined(_WIN32)",
    "#if defined(_WIN32)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_raw_socket_linux_local_mac.c", "stub_raw_socket_linux_nonblock.c"],
    "#if defined(__linux__)",
    "#if !defined(__linux__)",
    true,
  )
  push_source_platform_gate_rules(
    rules,
    ["stub_raw_socket_unsupported_live_ext.c"],
    "#if !defined(__linux__)",
    "#if defined(__linux__)",
    true,
  )
  rules
}

///|
pub fn native_stub_expected_source_platform_gate_facts() -> Array[
  NativeStubSourcePlatformGateFact,
] {
  let facts : Array[NativeStubSourcePlatformGateFact] = []
  for rule in native_stub_source_platform_gate_rules() {
    facts.push({
      file: rule.file,
      required_gate_present: true,
      forbidden_gate_present: false,
      gate_closed: true,
      export_present: rule.require_export,
      export_before_required_gate: false,
    })
  }
  facts
}

///|
pub fn native_stub_source_platform_gate_policy_report(
  actual : Array[NativeStubSourcePlatformGateFact],
) -> NativeStubSourcePlatformGatePolicyReport {
  native_stub_source_platform_gate_policy_report_from_rules(
    native_stub_source_platform_gate_rules(),
    actual,
  )
}

///|
pub fn native_stub_source_platform_gate_policy_report_from_rules(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> NativeStubSourcePlatformGatePolicyReport {
  {
    rule_count: rules.length(),
    actual_file_count: count_actual_platform_gate_files(actual),
    missing_file_count: count_missing_platform_gate_files(rules, actual),
    extra_file_count: count_extra_platform_gate_files(rules, actual),
    required_gate_count: count_required_platform_gates(rules),
    forbidden_gate_count: count_forbidden_platform_gates(rules),
    required_export_count: count_required_platform_gate_exports(rules),
    missing_required_gate_count: count_missing_required_platform_gates(
      rules, actual,
    ),
    present_forbidden_gate_count: count_present_forbidden_platform_gates(
      rules, actual,
    ),
    unclosed_gate_count: count_unclosed_platform_gates(rules, actual),
    missing_required_export_count: count_missing_required_platform_gate_exports(
      rules, actual,
    ),
    export_before_gate_count: count_exports_before_platform_gate(rules, actual),
    duplicate_gate_fact_count: count_duplicate_platform_gate_facts(actual),
    replaces_file_reading: false,
    replaces_gate_extraction: false,
    live_io_evidence: false,
  }
}

///|
fn source_platform_gate_rule(
  file : String,
  required_gate : String,
  forbidden_gate : String,
  require_export : Bool,
) -> NativeStubSourcePlatformGateRule {
  { file, required_gate, forbidden_gate, require_export }
}

///|
fn push_source_platform_gate_rules(
  rules : Array[NativeStubSourcePlatformGateRule],
  files : Array[String],
  required_gate : String,
  forbidden_gate : String,
  require_export : Bool,
) -> Unit {
  for file in files {
    rules.push(
      source_platform_gate_rule(
        file, required_gate, forbidden_gate, require_export,
      ),
    )
  }
}

///|
fn count_actual_platform_gate_files(
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let files : Array[String] = []
  for fact in facts {
    if !source_platform_contains_string(files, fact.file) {
      files.push(fact.file)
    }
  }
  files.length()
}

///|
fn count_missing_platform_gate_files(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let mut count = 0
  for rule in rules {
    if !source_platform_contains_file(actual, rule.file) {
      count += 1
    }
  }
  count
}

///|
fn count_extra_platform_gate_files(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let seen : Array[String] = []
  let mut count = 0
  for fact in actual {
    if !source_platform_contains_string(seen, fact.file) {
      seen.push(fact.file)
      if !source_platform_contains_rule_file(rules, fact.file) {
        count += 1
      }
    }
  }
  count
}

///|
fn count_required_platform_gates(
  rules : Array[NativeStubSourcePlatformGateRule],
) -> Int {
  let mut count = 0
  for rule in rules {
    if rule.required_gate != "" {
      count += 1
    }
  }
  count
}

///|
fn count_forbidden_platform_gates(
  rules : Array[NativeStubSourcePlatformGateRule],
) -> Int {
  let mut count = 0
  for rule in rules {
    if rule.forbidden_gate != "" {
      count += 1
    }
  }
  count
}

///|
fn count_required_platform_gate_exports(
  rules : Array[NativeStubSourcePlatformGateRule],
) -> Int {
  let mut count = 0
  for rule in rules {
    if rule.require_export {
      count += 1
    }
  }
  count
}

///|
fn count_missing_required_platform_gates(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let mut count = 0
  for rule in rules {
    if !source_platform_rule_has_required_gate_fact(rule, actual) {
      count += 1
    }
  }
  count
}

///|
fn count_present_forbidden_platform_gates(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let mut count = 0
  for rule in rules {
    if rule.forbidden_gate != "" &&
      source_platform_rule_has_forbidden_gate_fact(rule, actual) {
      count += 1
    }
  }
  count
}

///|
fn count_unclosed_platform_gates(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let mut count = 0
  for rule in rules {
    if source_platform_rule_has_unclosed_gate_fact(rule, actual) {
      count += 1
    }
  }
  count
}

///|
fn count_missing_required_platform_gate_exports(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let mut count = 0
  for rule in rules {
    if rule.require_export &&
      !source_platform_rule_has_required_export_fact(rule, actual) {
      count += 1
    }
  }
  count
}

///|
fn count_exports_before_platform_gate(
  rules : Array[NativeStubSourcePlatformGateRule],
  actual : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let mut count = 0
  for rule in rules {
    if source_platform_rule_has_export_before_gate_fact(rule, actual) {
      count += 1
    }
  }
  count
}

///|
fn count_duplicate_platform_gate_facts(
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Int {
  let files : Array[String] = []
  let mut count = 0
  for fact in facts {
    if source_platform_contains_string(files, fact.file) {
      count += 1
    } else {
      files.push(fact.file)
    }
  }
  count
}

///|
fn source_platform_rule_has_required_gate_fact(
  rule : NativeStubSourcePlatformGateRule,
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Bool {
  for fact in facts {
    if fact.file == rule.file && fact.required_gate_present {
      return true
    }
  }
  false
}

///|
fn source_platform_rule_has_forbidden_gate_fact(
  rule : NativeStubSourcePlatformGateRule,
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Bool {
  for fact in facts {
    if fact.file == rule.file && fact.forbidden_gate_present {
      return true
    }
  }
  false
}

///|
fn source_platform_rule_has_unclosed_gate_fact(
  rule : NativeStubSourcePlatformGateRule,
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Bool {
  for fact in facts {
    if fact.file == rule.file && !fact.gate_closed {
      return true
    }
  }
  false
}

///|
fn source_platform_rule_has_required_export_fact(
  rule : NativeStubSourcePlatformGateRule,
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Bool {
  for fact in facts {
    if fact.file == rule.file && fact.export_present {
      return true
    }
  }
  false
}

///|
fn source_platform_rule_has_export_before_gate_fact(
  rule : NativeStubSourcePlatformGateRule,
  facts : Array[NativeStubSourcePlatformGateFact],
) -> Bool {
  for fact in facts {
    if fact.file == rule.file && fact.export_before_required_gate {
      return true
    }
  }
  false
}

///|
fn source_platform_contains_rule_file(
  rules : Array[NativeStubSourcePlatformGateRule],
  file : String,
) -> Bool {
  for rule in rules {
    if rule.file == file {
      return true
    }
  }
  false
}

///|
fn source_platform_contains_file(
  facts : Array[NativeStubSourcePlatformGateFact],
  file : String,
) -> Bool {
  for fact in facts {
    if fact.file == file {
      return true
    }
  }
  false
}

///|
fn source_platform_contains_string(
  items : Array[String],
  item : String,
) -> Bool {
  for got in items {
    if got == item {
      return true
    }
  }
  false
}