//! Device attestation type for credential verification.
//!
//! This module provides the `DeviceAttestation` struct representing hardware-level
//! security information about the presenting device from the KB-JWT.

use serde::{Deserialize, Serialize};

/// Device attestation data from KB-JWT.
///
/// Provides hardware-level security information about the presenting device,
/// including secure element presence, TEE availability, and integrity status.
///
/// # Fields
///
/// The attestation includes platform-specific security indicators:
///
/// - **iOS**: App Attest support, Secure Enclave
/// - **Android**: Key attestation, bootloader status, TEE
/// - **All Platforms**: Emulator detection, debug detection, encryption status
///
/// # Example
///
/// ```rust
/// use lastid_sdk::types::DeviceAttestation;
///
/// let attestation = DeviceAttestation::default();
/// assert!(!attestation.has_secure_element);
/// assert_eq!(attestation.integrity_status, "unknown");
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[allow(clippy::struct_excessive_bools)]
pub struct DeviceAttestation {
    /// Device has a secure element (e.g., Apple Secure Enclave, Titan M).
    #[serde(default)]
    pub has_secure_element: bool,

    /// Device has a Trusted Execution Environment.
    #[serde(default)]
    pub has_tee: bool,

    /// Device integrity verification result: "pass", "fail", "unknown".
    #[serde(default = "default_integrity_status")]
    pub integrity_status: String,

    /// Specific integrity indicators (platform-specific).
    ///
    /// Examples: "MEETS\_BASIC\_INTEGRITY", "MEETS\_DEVICE\_INTEGRITY" (Android)
    #[serde(default)]
    pub integrity_indicators: Vec<String>,

    /// Device is running in an emulator.
    #[serde(default)]
    pub is_emulator: bool,

    /// App is being debugged.
    #[serde(default)]
    pub is_debugged: bool,

    /// Bootloader is locked (Android).
    #[serde(default)]
    pub bootloader_locked: bool,

    /// Device storage is encrypted.
    #[serde(default)]
    pub device_encrypted: bool,

    /// Hardware-derived fingerprint (if available).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hardware_fingerprint: Option<String>,

    /// iOS App Attest is supported.
    #[serde(default)]
    pub app_attest_supported: bool,

    /// iOS App Attest key ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub app_attest_key_id: Option<String>,

    /// Android key attestation security level.
    ///
    /// Values: "Software", "`TrustedEnvironment`", "`StrongBox`"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_attestation_security_level: Option<String>,

    /// Device model.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,

    /// Operating system version.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub os_version: Option<String>,

    /// Timestamp when attestation was collected (Unix epoch seconds).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub collected_at: Option<i64>,
}

fn default_integrity_status() -> String {
    "unknown".to_string()
}

impl Default for DeviceAttestation {
    fn default() -> Self {
        Self {
            has_secure_element: false,
            has_tee: false,
            integrity_status: default_integrity_status(),
            integrity_indicators: Vec::new(),
            is_emulator: false,
            is_debugged: false,
            bootloader_locked: false,
            device_encrypted: false,
            hardware_fingerprint: None,
            app_attest_supported: false,
            app_attest_key_id: None,
            key_attestation_security_level: None,
            model: None,
            os_version: None,
            collected_at: None,
        }
    }
}

impl DeviceAttestation {
    /// Check if the device has high security (secure element or TEE).
    #[must_use]
    pub const fn is_high_security(&self) -> bool {
        self.has_secure_element || self.has_tee
    }

    /// Check if the device passed integrity verification.
    #[must_use]
    pub fn integrity_passed(&self) -> bool {
        self.integrity_status == "pass"
    }

    /// Check if the device is potentially compromised.
    ///
    /// Returns `true` if any of the following are detected:
    /// - Running in an emulator
    /// - Being debugged
    /// - Bootloader unlocked
    /// - Integrity check failed
    #[must_use]
    pub fn is_potentially_compromised(&self) -> bool {
        self.is_emulator
            || self.is_debugged
            || !self.bootloader_locked
            || self.integrity_status == "fail"
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default() {
        let attestation = DeviceAttestation::default();
        assert!(!attestation.has_secure_element);
        assert!(!attestation.has_tee);
        assert_eq!(attestation.integrity_status, "unknown");
        assert!(attestation.integrity_indicators.is_empty());
        assert!(!attestation.is_emulator);
        assert!(!attestation.is_debugged);
        assert!(!attestation.bootloader_locked);
        assert!(!attestation.device_encrypted);
    }

    #[test]
    fn test_is_high_security() {
        let mut attestation = DeviceAttestation::default();
        assert!(!attestation.is_high_security());

        attestation.has_secure_element = true;
        assert!(attestation.is_high_security());

        attestation.has_secure_element = false;
        attestation.has_tee = true;
        assert!(attestation.is_high_security());
    }

    #[test]
    fn test_integrity_passed() {
        let mut attestation = DeviceAttestation::default();
        assert!(!attestation.integrity_passed());

        attestation.integrity_status = "pass".to_string();
        assert!(attestation.integrity_passed());

        attestation.integrity_status = "fail".to_string();
        assert!(!attestation.integrity_passed());
    }

    #[test]
    fn test_is_potentially_compromised() {
        let mut attestation = DeviceAttestation::default();
        attestation.bootloader_locked = true;
        assert!(!attestation.is_potentially_compromised());

        attestation.is_emulator = true;
        assert!(attestation.is_potentially_compromised());

        attestation.is_emulator = false;
        attestation.is_debugged = true;
        assert!(attestation.is_potentially_compromised());

        attestation.is_debugged = false;
        attestation.integrity_status = "fail".to_string();
        assert!(attestation.is_potentially_compromised());
    }

    #[test]
    fn test_serialization() {
        let attestation = DeviceAttestation {
            has_secure_element: true,
            has_tee: true,
            integrity_status: "pass".to_string(),
            integrity_indicators: vec!["MEETS_DEVICE_INTEGRITY".to_string()],
            is_emulator: false,
            is_debugged: false,
            bootloader_locked: true,
            device_encrypted: true,
            hardware_fingerprint: Some("abc123".to_string()),
            app_attest_supported: true,
            app_attest_key_id: Some("key123".to_string()),
            key_attestation_security_level: Some("StrongBox".to_string()),
            model: Some("Pixel 7".to_string()),
            os_version: Some("14".to_string()),
            collected_at: Some(1705881600),
        };

        let json = serde_json::to_string(&attestation).unwrap();
        let deserialized: DeviceAttestation = serde_json::from_str(&json).unwrap();
        assert_eq!(attestation, deserialized);
    }

    #[test]
    fn test_deserialization_with_defaults() {
        let json = r#"{"has_secure_element": true}"#;
        let attestation: DeviceAttestation = serde_json::from_str(json).unwrap();

        assert!(attestation.has_secure_element);
        assert!(!attestation.has_tee);
        assert_eq!(attestation.integrity_status, "unknown");
    }
}
