///|
/// PROFINET Security Mode (§6.4.1.4 Table 117).
pub(all) enum SecurityMode {
  /// Allows secure and plain ARs (default).
  Any
  /// Demands secure ARs only.
  Protected
} derive(Eq, Debug)

///|
pub fn parse_security_mode(value : Int) -> SecurityMode {
  match value {
    0x02 => Protected
    _ => Any
  }
}

///|
pub fn security_mode_value(mode : SecurityMode) -> Int {
  match mode {
    Any => 0x01
    Protected => 0x02
  }
}

///|
/// Protection mode of the security data header (§6.2.1.2 Table 74).
pub(all) enum ProtectionMode {
  /// Authentication only – SecurityChecksum appended, data in cleartext.
  AuthOnly
  /// Authenticated encryption – payload encrypted + authenticated.
  AuthEncrypt
} derive(Eq, Debug)

///|
pub fn parse_protection_mode(value : Int) -> ProtectionMode {
  match value {
    0x01 => AuthEncrypt
    _ => AuthOnly
  }
}

///|
pub fn protection_mode_value(mode : ProtectionMode) -> Int {
  match mode {
    AuthOnly => 0x00
    AuthEncrypt => 0x01
  }
}

///|
/// AEAD algorithm (§6.2.2 Tables 99-102).
pub(all) enum AeadAlgorithm {
  AES_128_GCM
  AES_256_GCM
  ChaCha20_Poly1305
} derive(Eq, Debug)

///|
pub fn parse_aead_algorithm(value : Int) -> AeadAlgorithm {
  match value {
    0x00 => AES_128_GCM
    0x01 => AES_256_GCM
    _ => ChaCha20_Poly1305
  }
}

///|
pub fn aead_key_length(alg : AeadAlgorithm) -> Int {
  match alg {
    AES_128_GCM => 16
    AES_256_GCM => 32
    ChaCha20_Poly1305 => 32
  }
}

///|
/// Security capability utilization (Table 98).
pub(all) enum SecurityUtilization {
  SymmetricAuthOnly
  SymmetricAuthEncrypt
  KeyDerivation
  KeyAgreement
  SignatureVerification
  SignatureGeneration
} derive(Eq, Debug)

///|
pub fn parse_security_utilization(value : Int) -> SecurityUtilization {
  match value {
    0x01 => SymmetricAuthOnly
    0x02 => SymmetricAuthEncrypt
    0x03 => KeyDerivation
    0x04 => KeyAgreement
    0x05 => SignatureVerification
    _ => SignatureGeneration
  }
}

///|
/// SecurityData header (§6.2.1.1 Table 73).
pub(all) struct SecurityData {
  /// ProtectionMode (0=auth only, 1=auth+encrypt).
  protection_mode : ProtectionMode
  /// GenerationNumber (bits 0-3 of SecurityControl).
  generation_number : Int
  /// SecuritySequenceCounter (Unsigned32, 1..0xFFFFFFFF).
  sequence_counter : Int
  /// SecurityLength (Unsigned16, payload length).
  security_length : Int
} derive(Eq, Debug)

///|
/// Encode the 8-byte SecurityData header.
pub fn encode_security_data(sd : SecurityData) -> Bytes {
  let out : Array[Byte] = []
  // Byte 0: SecurityInformation.ProtectionMode
  out.push(protection_mode_value(sd.protection_mode).to_byte())
  // Byte 1: SecurityControl (GenerationNumber in bits 0-3)
  out.push((sd.generation_number & 0x0F).to_byte())
  // Bytes 2-5: SecuritySequenceCounter (big-endian)
  out.push(((sd.sequence_counter >> 24) & 0xFF).to_byte())
  out.push(((sd.sequence_counter >> 16) & 0xFF).to_byte())
  out.push(((sd.sequence_counter >> 8) & 0xFF).to_byte())
  out.push((sd.sequence_counter & 0xFF).to_byte())
  // Bytes 6-7: SecurityLength (big-endian)
  out.push(((sd.security_length >> 8) & 0xFF).to_byte())
  out.push((sd.security_length & 0xFF).to_byte())
  Bytes::from_array(out)
}

///|
/// Parse the 8-byte SecurityData header.
pub fn parse_security_data(
  data : Bytes,
  offset : Int,
) -> SecurityData raise Error {
  guard data.length() >= offset + 8 else {
    fail("SecurityData: need 8 bytes, got \{data.length() - offset}")
  }
  let protection_mode = parse_protection_mode(data[offset].to_int())
  let generation_number = data[offset + 1].to_int() & 0x0F
  let sequence_counter = (data[offset + 2].to_int() << 24) |
    (data[offset + 3].to_int() << 16) |
    (data[offset + 4].to_int() << 8) |
    data[offset + 5].to_int()
  let security_length = (data[offset + 6].to_int() << 8) |
    data[offset + 7].to_int()
  SecurityData::{
    protection_mode,
    generation_number,
    sequence_counter,
    security_length,
  }
}

///|
/// Security Frame IDs.
pub let frame_id_alarm_high_secure : Int = 0xFC41

///|
pub let frame_id_alarm_low_secure : Int = 0xFE41

///|
/// SXP Security Block Types.
pub let block_type_read_security_req : Int = 0x0723

///|
pub let block_type_read_security_rsp : Int = 0x8723

///|
pub let block_type_write_security_req : Int = 0x0724

///|
pub let block_type_write_security_rsp : Int = 0x8724

///|
/// Format security data header summary.
pub fn format_security_data(sd : SecurityData) -> String {
  let mode_str = match sd.protection_mode {
    AuthOnly => "AuthOnly"
    AuthEncrypt => "AuthEncrypt"
  }
  "mode=" +
  mode_str +
  " gen=" +
  sd.generation_number.to_string() +
  " seq=" +
  sd.sequence_counter.to_string() +
  " len=" +
  sd.security_length.to_string()
}

///|
/// Format security mode information.
pub fn format_security_info(mode : SecurityMode, alg : AeadAlgorithm) -> String {
  let mode_str = match mode {
    Any => "ANY"
    Protected => "PROTECTED"
  }
  let alg_str = match alg {
    AES_128_GCM => "AEAD_AES_128_GCM"
    AES_256_GCM => "AEAD_AES_256_GCM"
    ChaCha20_Poly1305 => "AEAD_CHACHA20_POLY1305"
  }
  "SecurityMode=" +
  mode_str +
  " Algorithm=" +
  alg_str +
  " KeyLen=" +
  aead_key_length(alg).to_string()
}

///|
/// Security service class identifiers (§5.2).
pub(all) enum SecurityServiceClass {
  /// PRO — Frame-level protection/deprotection.
  Protection
  /// SAM — EAP-TLS handshake management.
  SecurityAssociationMgmt
  /// SCM — Certificate/credential online configuration.
  SecurityConfigMgmt
  /// CRV — Local credential store and crypto operations.
  ConfigurationVault
  /// ACD — Access control decision.
  AccessControlDecision
  /// RAZ — Role authorization queries.
  RoleAuthorization
} derive(Eq, Debug)

///|
pub fn security_service_class_label(sc : SecurityServiceClass) -> String {
  match sc {
    Protection => "PRO"
    SecurityAssociationMgmt => "SAM"
    SecurityConfigMgmt => "SCM"
    ConfigurationVault => "CRV"
    AccessControlDecision => "ACD"
    RoleAuthorization => "RAZ"
  }
}

///|
/// PRO state machine states (§6.2.3, Figure 8).
pub(all) enum ProState {
  ProIdle
  ProOpen
  ProDegraded
  ProClosed
} derive(Eq, Debug)

///|
/// PRO state machine events.
pub(all) enum ProEvent {
  CreateSecurityAssociation
  RemoveSecurityAssociation
  SackDegradationDetected
  UpdateSecurityAssociation
} derive(Eq, Debug)

///|
/// Transition the PRO state machine.
pub fn pro_transition(state : ProState, event : ProEvent) -> ProState {
  match (state, event) {
    (ProIdle, CreateSecurityAssociation) => ProOpen
    (ProOpen, RemoveSecurityAssociation) => ProClosed
    (ProOpen, SackDegradationDetected) => ProDegraded
    (ProOpen, UpdateSecurityAssociation) => ProOpen
    (ProDegraded, UpdateSecurityAssociation) => ProOpen
    (ProDegraded, RemoveSecurityAssociation) => ProClosed
    (ProClosed, CreateSecurityAssociation) => ProOpen
    _ => state
  }
}

///|
/// SMPM state machine states (§6.2.3, Figure 9).
pub(all) enum SmpmState {
  SmpmIdle
  SmpmPending
  SmpmEstablished
  SmpmRekeying
} derive(Eq, Debug)

///|
/// SMPM state machine events.
pub(all) enum SmpmEvent {
  InitHandshake
  HandshakeComplete
  RekeyRequest
  RekeyComplete
  SessionTerminate
} derive(Eq, Debug)

///|
/// Transition the SMPM state machine.
pub fn smpm_transition(state : SmpmState, event : SmpmEvent) -> SmpmState {
  match (state, event) {
    (SmpmIdle, InitHandshake) => SmpmPending
    (SmpmPending, HandshakeComplete) => SmpmEstablished
    (SmpmEstablished, RekeyRequest) => SmpmRekeying
    (SmpmRekeying, RekeyComplete) => SmpmEstablished
    (SmpmEstablished, SessionTerminate) => SmpmIdle
    (SmpmRekeying, SessionTerminate) => SmpmIdle
    _ => state
  }
}

///|
/// CMSAM / CTLSAM state machine states (§6.3.3, Figures 10-11).
pub(all) enum CmsamState {
  CmsamIdle
  CmsamEapStart
  CmsamEapRunning
  CmsamKeysReady
  CmsamComplete
} derive(Eq, Debug)

///|
/// CMSAM / CTLSAM events.
pub(all) enum CmsamEvent {
  EapStart
  EapMessageReceived
  KeysReady
  Complete
  Abort
} derive(Eq, Debug)

///|
/// Transition the CMSAM/CTLSAM state machine.
pub fn cmsam_transition(state : CmsamState, event : CmsamEvent) -> CmsamState {
  match (state, event) {
    (CmsamIdle, EapStart) => CmsamEapStart
    (CmsamEapStart, EapMessageReceived) => CmsamEapRunning
    (CmsamEapRunning, EapMessageReceived) => CmsamEapRunning
    (CmsamEapRunning, KeysReady) => CmsamKeysReady
    (CmsamKeysReady, Complete) => CmsamComplete
    (_, Abort) => CmsamIdle
    _ => state
  }
}

// ─── Security Credential Model ───

///|
/// End-entity credential: EE certificate path + private key reference.
pub(all) struct SecurityCredential {
  ee_cert_path : String
  private_key_ref : String
  trust_anchor_ca : String
} derive(Eq, Debug)

///|
pub fn format_credential(cred : SecurityCredential) -> String {
  let lines : Array[String] = []
  lines.push("=== Security Credential ===")
  lines.push("EE CertPath: " + cred.ee_cert_path)
  lines.push("PrivateKey:  " + cred.private_key_ref)
  lines.push("TrustAnchor: " + cred.trust_anchor_ca)
  lines.join("\n")
}

// ─── ACD — Access Control Decision (§6.4) ───

///|
/// Access control target categories.
pub(all) enum AcdTarget {
  PlainAR
  SecureAR
  RDCP
  Control
  Record
  SCM
  DCP
} derive(Eq, Debug)

///|
/// ACD decision result.
pub(all) enum AcdResult {
  Permit
  Deny
} derive(Eq, Debug)

///|
/// Evaluate access-control decision.
/// In Protected mode, only SecureAR is permitted for AR establishment;
/// plain control/record/DCP traffic is denied.
pub fn acd_check(mode : SecurityMode, target : AcdTarget) -> AcdResult {
  match mode {
    Any => Permit
    Protected =>
      match target {
        SecureAR => Permit
        SCM => Permit
        _ => Deny
      }
  }
}

// ─── CTLSAM — Controller-side TLS Security Association Manager (§6.3.3 Figure 11) ───

///|
/// CTLSAM states (TLS-specific variant of CMSAM).
pub(all) enum CtlsamState {
  CtlsamIdle
  CtlsamTlsInit
  CtlsamTlsHandshake
  CtlsamKeysReady
  CtlsamComplete
} derive(Eq, Debug)

///|
/// CTLSAM events.
pub(all) enum CtlsamEvent {
  TlsStart
  TlsMessageReceived
  TlsKeysReady
  TlsComplete
  TlsAbort
} derive(Eq, Debug)

///|
/// Transition the CTLSAM state machine.
pub fn ctlsam_transition(
  state : CtlsamState,
  event : CtlsamEvent,
) -> CtlsamState {
  match (state, event) {
    (CtlsamIdle, TlsStart) => CtlsamTlsInit
    (CtlsamTlsInit, TlsMessageReceived) => CtlsamTlsHandshake
    (CtlsamTlsHandshake, TlsMessageReceived) => CtlsamTlsHandshake
    (CtlsamTlsHandshake, TlsKeysReady) => CtlsamKeysReady
    (CtlsamKeysReady, TlsComplete) => CtlsamComplete
    (_, TlsAbort) => CtlsamIdle
    _ => state
  }
}

// ─── Security Record Indices (§8) ───

///|
/// Index range for security configuration records.
pub let security_record_index_start : Int = 0xC000

///|
pub let security_record_index_end : Int = 0xC0FF

///|
/// New BlockType values for security blocks.
pub(all) enum SecurityBlockType {
  SecurityCapabilities
  SecurityConfiguration
  CertificationPath
  TrustedCA
  KeyPairCSR
} derive(Eq, Debug)

///|
pub fn security_block_type_value(bt : SecurityBlockType) -> Int {
  match bt {
    SecurityCapabilities => 0x0600
    SecurityConfiguration => 0x0601
    CertificationPath => 0x0602
    TrustedCA => 0x0603
    KeyPairCSR => 0x0604
  }
}

// ─── PNIOStatus Security Error Codes ───

///|
/// Security-related ErrorCode2 extensions.
pub(all) enum SecurityErrorCode {
  NoSecurityAssociation
  HandshakeFailed
  CertificateInvalid
  AccessDenied
  ReplayDetected
  IntegrityError
} derive(Eq, Debug)

///|
pub fn security_error_code_value(code : SecurityErrorCode) -> Int {
  match code {
    NoSecurityAssociation => 0x80
    HandshakeFailed => 0x81
    CertificateInvalid => 0x82
    AccessDenied => 0x83
    ReplayDetected => 0x84
    IntegrityError => 0x85
  }
}

///|
pub fn security_error_code_label(code : SecurityErrorCode) -> String {
  match code {
    NoSecurityAssociation => "No security association"
    HandshakeFailed => "Handshake failed"
    CertificateInvalid => "Certificate invalid"
    AccessDenied => "Access denied"
    ReplayDetected => "Replay detected"
    IntegrityError => "Integrity error"
  }
}