//! Verified credential type definition.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::trust_registry::IssuerStatus;
use crate::types::{DeviceAttestation, DeviceSecurity, FieldVerificationMetadata};

/// Verified credential with extracted claims.
///
/// Returned after successful verification of a credential presentation.
/// Contains the subject DID, issuer DID, credential type, claims, and issuer
/// status.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifiedCredential {
    /// Subject DID (the credential holder)
    pub subject_did: String,

    /// Issuer DID (the credential issuer)
    pub issuer_did: String,

    /// Credential type (e.g., "LastID.Base", "LastID.VerifiedEmail")
    pub credential_type: String,

    /// Extracted claims from the credential
    pub claims: serde_json::Value,

    /// Credential issuance time
    pub issued_at: DateTime<Utc>,

    /// Credential expiration time
    pub expires_at: DateTime<Utc>,

    /// Issuer status from trust registry
    pub issuer_status: IssuerStatus,

    /// Whether the cryptographic signature was verified
    ///
    /// Always `true` for credentials returned from `verify_presentation()` -
    /// if signature verification fails, an error is returned instead.
    pub signature_verified: bool,
}

impl VerifiedCredential {
    /// Check if the credential is expired.
    #[must_use]
    pub fn is_expired(&self) -> bool {
        Utc::now() >= self.expires_at
    }

    /// Get a claim value by key.
    #[must_use]
    pub fn get_claim(&self, key: &str) -> Option<&serde_json::Value> {
        self.claims.get(key)
    }

    /// Get a claim as a string.
    #[must_use]
    pub fn get_claim_str(&self, key: &str) -> Option<&str> {
        self.claims.get(key).and_then(|v| v.as_str())
    }

    /// Get a claim as an i64.
    #[must_use]
    pub fn get_claim_i64(&self, key: &str) -> Option<i64> {
        self.claims.get(key).and_then(serde_json::Value::as_i64)
    }

    /// Get a claim as a boolean.
    #[must_use]
    pub fn get_claim_bool(&self, key: &str) -> Option<bool> {
        self.claims.get(key).and_then(serde_json::Value::as_bool)
    }

    // ============================================
    // Typed Accessors (FR-009 - FR-014)
    // ============================================

    /// Get email as a single value.
    ///
    /// If stored as an array, returns the first element.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// let email = credential.email();
    /// if let Some(e) = email {
    ///     println!("Email: {}", e);
    /// }
    /// ```
    #[must_use]
    pub fn email(&self) -> Option<&str> {
        self.get_claim_single_str("email")
    }

    /// Get all email addresses.
    ///
    /// Returns a single-element vec if stored as a string.
    #[must_use]
    pub fn emails(&self) -> Vec<&str> {
        self.get_claim_array_str("email")
    }

    /// Get phone number as a single value.
    ///
    /// If stored as an array, returns the first element.
    #[must_use]
    pub fn phone_number(&self) -> Option<&str> {
        self.get_claim_single_str("phone_number")
            .or_else(|| self.get_claim_single_str("phone"))
    }

    /// Get all phone numbers.
    ///
    /// Returns a single-element vec if stored as a string.
    #[must_use]
    pub fn phone_numbers(&self) -> Vec<&str> {
        let mut result = self.get_claim_array_str("phone_number");
        if result.is_empty() {
            result = self.get_claim_array_str("phone");
        }
        result
    }

    /// Get display name.
    #[must_use]
    pub fn display_name(&self) -> Option<&str> {
        self.get_claim_str("display_name")
    }

    /// Get given (first) name.
    #[must_use]
    pub fn given_name(&self) -> Option<&str> {
        self.get_claim_str("given_name")
    }

    /// Get family (last) name.
    #[must_use]
    pub fn family_name(&self) -> Option<&str> {
        self.get_claim_str("family_name")
    }

    /// Get company name.
    #[must_use]
    pub fn company(&self) -> Option<&str> {
        self.get_claim_str("company")
    }

    /// Get job title.
    #[must_use]
    pub fn job_title(&self) -> Option<&str> {
        self.get_claim_str("job_title")
            .or_else(|| self.get_claim_str("title"))
    }

    /// Get credential issuance timestamp.
    ///
    /// This is the `iat` claim from the credential, aliased to the `issued_at`
    /// field.
    #[must_use]
    pub const fn verified_at(&self) -> Option<DateTime<Utc>> {
        Some(self.issued_at)
    }

    /// Get device attestation data from KB-JWT.
    ///
    /// Returns `None` if device attestation is not present in the credential
    /// claims.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// if let Some(attestation) = credential.device_attestation() {
    ///     if attestation.is_high_security() {
    ///         println!("High security device");
    ///     }
    /// }
    /// ```
    #[must_use]
    pub fn device_attestation(&self) -> Option<DeviceAttestation> {
        self.claims
            .get("device_attestation")
            .and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    /// Get device security context from KB-JWT.
    ///
    /// Returns `None` if device security context is not present in the
    /// credential claims.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// if let Some(security) = credential.device_security() {
    ///     if security.is_high_security() && security.biometric_used {
    ///         println!("High security with biometric");
    ///     }
    /// }
    /// ```
    #[must_use]
    pub fn device_security(&self) -> Option<DeviceSecurity> {
        self.claims
            .get("device_security")
            .and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    /// Get verification metadata for a specific field.
    ///
    /// # Arguments
    ///
    /// * `field_name` - The field to get verification for (e.g., "email",
    ///   "phone")
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// if let Some(meta) = credential.field_verification("email") {
    ///     if meta.verified {
    ///         println!("Email verified by: {:?}", meta.provider);
    ///     }
    /// }
    /// ```
    #[must_use]
    pub fn field_verification(&self, field_name: &str) -> Option<FieldVerificationMetadata> {
        self.claims
            .get("field_verification")
            .and_then(|v| v.get(field_name))
            .and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    // ============================================
    // Internal Helpers for Array Normalization
    // ============================================

    /// Get a claim as a single string (first element if array).
    fn get_claim_single_str(&self, key: &str) -> Option<&str> {
        match self.claims.get(key) {
            Some(Value::String(s)) => Some(s.as_str()),
            Some(Value::Array(arr)) => arr.first().and_then(Value::as_str),
            _ => None,
        }
    }

    /// Get a claim as an array of strings (wrap single value in vec).
    fn get_claim_array_str(&self, key: &str) -> Vec<&str> {
        match self.claims.get(key) {
            Some(Value::String(s)) => vec![s.as_str()],
            Some(Value::Array(arr)) => arr.iter().filter_map(Value::as_str).collect(),
            _ => vec![],
        }
    }
}

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

    fn test_credential() -> VerifiedCredential {
        VerifiedCredential {
            subject_did: "did:lastid:user123".into(),
            issuer_did: "did:lastid:issuer456".into(),
            credential_type: "LastID.VerifiedEmail".into(),
            claims: serde_json::json!({
                "email": "user@example.com",
                "verified": true,
                "trust_level": 3
            }),
            issued_at: Utc::now(),
            expires_at: Utc::now() + chrono::Duration::hours(24),
            issuer_status: IssuerStatus::Authorized,
            signature_verified: true,
        }
    }

    #[test]
    fn test_not_expired() {
        let cred = test_credential();
        assert!(!cred.is_expired());
    }

    #[test]
    fn test_expired() {
        let mut cred = test_credential();
        cred.expires_at = Utc::now() - chrono::Duration::hours(1);
        assert!(cred.is_expired());
    }

    #[test]
    fn test_get_claim_str() {
        let cred = test_credential();
        assert_eq!(cred.get_claim_str("email"), Some("user@example.com"));
        assert_eq!(cred.get_claim_str("nonexistent"), None);
    }

    #[test]
    fn test_get_claim_bool() {
        let cred = test_credential();
        assert_eq!(cred.get_claim_bool("verified"), Some(true));
    }

    #[test]
    fn test_get_claim_i64() {
        let cred = test_credential();
        assert_eq!(cred.get_claim_i64("trust_level"), Some(3));
    }

    #[test]
    fn test_email_single() {
        let cred = test_credential();
        assert_eq!(cred.email(), Some("user@example.com"));
    }

    #[test]
    fn test_email_from_array() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "email": ["first@example.com", "second@example.com"]
        });
        assert_eq!(cred.email(), Some("first@example.com"));
    }

    #[test]
    fn test_emails_single() {
        let cred = test_credential();
        assert_eq!(cred.emails(), vec!["user@example.com"]);
    }

    #[test]
    fn test_emails_array() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "email": ["first@example.com", "second@example.com"]
        });
        assert_eq!(
            cred.emails(),
            vec!["first@example.com", "second@example.com"]
        );
    }

    #[test]
    fn test_phone_number() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "phone_number": "+1234567890"
        });
        assert_eq!(cred.phone_number(), Some("+1234567890"));
    }

    #[test]
    fn test_phone_fallback() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "phone": "+1234567890"
        });
        assert_eq!(cred.phone_number(), Some("+1234567890"));
    }

    #[test]
    fn test_display_name() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "display_name": "John Doe"
        });
        assert_eq!(cred.display_name(), Some("John Doe"));
    }

    #[test]
    fn test_given_family_name() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "given_name": "John",
            "family_name": "Doe"
        });
        assert_eq!(cred.given_name(), Some("John"));
        assert_eq!(cred.family_name(), Some("Doe"));
    }

    #[test]
    fn test_company_job_title() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "company": "Acme Corp",
            "job_title": "Engineer"
        });
        assert_eq!(cred.company(), Some("Acme Corp"));
        assert_eq!(cred.job_title(), Some("Engineer"));
    }

    #[test]
    fn test_job_title_fallback() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "title": "Manager"
        });
        assert_eq!(cred.job_title(), Some("Manager"));
    }

    #[test]
    fn test_verified_at() {
        let cred = test_credential();
        assert!(cred.verified_at().is_some());
        assert_eq!(cred.verified_at(), Some(cred.issued_at));
    }

    #[test]
    fn test_device_attestation() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "device_attestation": {
                "has_secure_element": true,
                "has_tee": false,
                "integrity_status": "pass"
            }
        });

        let attestation = cred.device_attestation().unwrap();
        assert!(attestation.has_secure_element);
        assert!(!attestation.has_tee);
        assert_eq!(attestation.integrity_status, "pass");
    }

    #[test]
    fn test_device_security() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "device_security": {
                "security_level": "high",
                "biometric_used": true,
                "platform": "ios"
            }
        });

        let security = cred.device_security().unwrap();
        assert!(security.is_high_security());
        assert!(security.biometric_used);
        assert_eq!(security.platform, "ios");
    }

    #[test]
    fn test_field_verification() {
        let mut cred = test_credential();
        cred.claims = serde_json::json!({
            "field_verification": {
                "email": {
                    "verified": true,
                    "provider": "google",
                    "method": "oauth"
                }
            }
        });

        let meta = cred.field_verification("email").unwrap();
        assert!(meta.verified);
        assert_eq!(meta.provider, Some("google".to_string()));
        assert_eq!(meta.method, Some("oauth".to_string()));
    }

    #[test]
    fn test_missing_optional_accessors() {
        let cred = test_credential();
        assert!(cred.display_name().is_none());
        assert!(cred.given_name().is_none());
        assert!(cred.family_name().is_none());
        assert!(cred.company().is_none());
        assert!(cred.device_attestation().is_none());
        assert!(cred.device_security().is_none());
        assert!(cred.field_verification("email").is_none());
    }
}
