//! Boundary types for JavaScript interop.
//!
//! This module defines the types exchanged with JavaScript from WASM methods.
//! All types use camelCase serialization for JavaScript conventions.
//!
//! TypeScript definitions are auto-generated by ts-rs. Run `cargo test` to
//! regenerate type definitions in the `bindings/` directory.

use serde::{Deserialize, Serialize};
#[cfg(test)]
use ts_rs::TS;

/// Status response for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct StatusResponse {
    /// Current status of the request.
    pub status: String,
    /// Request identifier.
    pub request_id: String,
    /// VP Token containing the credential (when fulfilled).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub presentation: Option<String>,
    /// Error message (when denied or notfound).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    /// Elapsed time in seconds (when timeout).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub elapsed_seconds: Option<u64>,
}

/// Response from `requestCredential`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct CredentialRequestResponse {
    /// Unique request identifier for polling.
    pub request_id: String,
    /// `OpenID4VP` request URI for QR code generation.
    pub request_uri: String,
    /// Seconds until this request expires.
    pub expires_in: u64,
    /// Nonce for cryptographic binding.
    pub nonce: String,
}

/// Verified credential response for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct VerifiedCredential {
    /// DID of the credential subject (the user).
    pub subject_did: String,
    /// DID of the credential issuer.
    pub issuer_did: String,
    /// Type of credential (e.g., "LastID.Base").
    pub credential_type: String,
    /// Credential claims as a JSON object.
    pub claims: serde_json::Value,
    /// ISO 8601 timestamp when credential was issued.
    pub issued_at: String,
    /// ISO 8601 timestamp when credential expires.
    pub expires_at: String,
    /// Issuer status from trust registry.
    pub issuer_status: String,
    /// Whether the cryptographic signature was verified.
    pub signature_verified: bool,
}

/// Issuer info response for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct IssuerInfo {
    /// Enterprise DID of the issuer.
    pub enterprise_did: String,
    /// Human-readable organization name.
    pub organization_name: String,
    /// Issuer's endpoint URL.
    pub endpoint_url: String,
    /// Current status (e.g., "Authorized", "Suspended").
    pub status: String,
    /// Credential types this issuer can issue.
    pub permitted_types: Vec<String>,
    /// Issuer's public key for signature verification.
    pub public_key: PublicKey,
}

/// Public key for signature verification.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct PublicKey {
    /// Key type (e.g., "EC").
    pub kty: String,
    /// Curve (e.g., "P-256").
    pub crv: String,
    /// X coordinate (base64url).
    pub x: String,
    /// Y coordinate (base64url).
    pub y: String,
}

/// Credential type info for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct CredentialTypeInfo {
    /// Internal type name (e.g., "LastID.Base").
    pub type_name: String,
    /// Human-readable display name.
    pub display_name: String,
    /// Description of the credential type.
    pub description: String,
    /// Fields included in this credential type.
    pub fields: Vec<String>,
}

/// Error codes for programmatic error handling.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[cfg_attr(test, ts(export))]
#[non_exhaustive]
pub enum ErrorCode {
    /// Configuration error (invalid endpoint, missing `client_id`, etc.)
    ConfigError,
    /// Network error (connection failed, timeout, DNS resolution)
    NetworkError,
    /// Authentication error (invalid credentials, token expired)
    AuthError,
    /// Policy validation error (missing required fields, invalid constraints)
    PolicyError,
    /// Credential verification error (signature invalid, expired, revoked)
    VerificationError,
    /// Trust registry error (issuer not found, suspended, unauthorized)
    TrustError,
    /// Request not found (404 from IDP)
    NotFoundError,
    /// Request expired (credential request timed out)
    ExpiredError,
    /// Request denied by user
    DeniedError,
    /// Rate limited by IDP
    RateLimitError,
    /// Internal SDK error (unexpected state)
    InternalError,
}

/// Structured error with code, message, and optional details.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct LastIDError {
    /// Error code for programmatic handling.
    pub code: ErrorCode,
    /// Human-readable error message.
    pub message: String,
    /// Additional error details (if available).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<String>,
    /// Whether this error type is retryable.
    pub is_retryable: bool,
    /// Suggested retry delay in milliseconds (if retryable).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub suggested_retry_ms: Option<u32>,
}

// =============================================================================
// Credential Filtering API Types (Feature 003)
// =============================================================================

/// Device attestation data for JavaScript.
///
/// Hardware-level security information about the presenting device from KB-JWT.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
#[allow(clippy::struct_excessive_bools)]
pub struct DeviceAttestation {
    /// Device has a secure element (e.g., Apple Secure Enclave, Titan M).
    pub has_secure_element: bool,
    /// Device has a Trusted Execution Environment.
    pub has_tee: bool,
    /// Device integrity verification result: "pass", "fail", "unknown".
    pub integrity_status: String,
    /// Specific integrity indicators (platform-specific).
    pub integrity_indicators: Vec<String>,
    /// Device is running in an emulator.
    pub is_emulator: bool,
    /// App is being debugged.
    pub is_debugged: bool,
    /// Bootloader is locked (Android).
    pub bootloader_locked: bool,
    /// Device storage is encrypted.
    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.
    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.
    #[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>,
}

/// Device security context for JavaScript.
///
/// High-level security assessment for policy decisions from KB-JWT.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct DeviceSecurity {
    /// Overall security level: "high", "medium", "low", "unknown".
    pub security_level: String,
    /// Whether biometric authentication was used for this presentation.
    pub biometric_used: bool,
    /// Platform identifier: "ios", "android", "web".
    pub platform: String,
}

/// Field verification metadata for JavaScript.
///
/// Describes how and when a specific credential field was verified.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct FieldVerificationMetadata {
    /// Whether the field value is verified.
    pub verified: bool,
    /// Verification provider (e.g., "google", "twilio", "lastid").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider: Option<String>,
    /// Verification method (e.g., "oauth", "sms\_otp", "email\_link").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub method: Option<String>,
    /// Timestamp when verification occurred (Unix epoch seconds).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verified_at: Option<i64>,
}

/// Verified response containing multiple credentials for JavaScript.
///
/// When a presentation contains multiple credentials (e.g., a chain like
/// Base → Persona → Employment), this struct provides structured access.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct VerifiedResponse {
    /// All verified credentials in the presentation.
    pub credentials: Vec<VerifiedCredential>,
    /// Shared device attestation (from KB-JWT).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub device_attestation: Option<DeviceAttestation>,
    /// Shared device security context (from KB-JWT).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub device_security: Option<DeviceSecurity>,
    /// Field verification metadata (from KB-JWT).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub field_verification: Option<serde_json::Value>,
}

// =============================================================================
// Parsed Credential Types (Feature 003 - User Story 6)
// =============================================================================

/// Parsed Persona credential for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct ParsedPersona {
    /// User's display name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub display_name: Option<String>,
    /// User's email address.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    /// User's phone number.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    /// Company/employer name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub company: Option<String>,
    /// Job title.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub job_title: Option<String>,
    /// Avatar URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_url: Option<String>,
    /// Website URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub website: Option<String>,
    /// Location.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,
    /// Visibility setting.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub visibility: Option<String>,
}

/// Parsed Employment credential for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct ParsedEmployment {
    /// Company/organization name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub company: Option<String>,
    /// Job title.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    /// Department.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub department: Option<String>,
    /// Employment start date.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub start_date: Option<String>,
    /// Employment end date.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub end_date: Option<String>,
    /// Whether this is current employment.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_current: Option<bool>,
    /// Job responsibilities.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub responsibilities: Option<String>,
}

/// Parsed `VerifiedEmail` credential for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct ParsedVerifiedEmail {
    /// Verified email address (required).
    pub email: String,
    /// Timestamp when email was verified (Unix epoch seconds).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verified_at: Option<i64>,
    /// Verification method (e.g., "oauth", "email\_link").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verification_method: Option<String>,
    /// Verification provider (e.g., "google", "microsoft").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider: Option<String>,
}

/// Parsed `VerifiedPhone` credential for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct ParsedVerifiedPhone {
    /// Verified phone number (required).
    pub phone_number: String,
    /// Timestamp when phone was verified (Unix epoch seconds).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verified_at: Option<i64>,
    /// Verification method (e.g., "sms\_otp", "voice\_call").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verification_method: Option<String>,
    /// Verification provider (e.g., "twilio", "lastid").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider: Option<String>,
}

/// Parsed `VerifiedPersona` credential for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct ParsedVerifiedPersona {
    /// User's display name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub display_name: Option<String>,
    /// User's given/first name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub given_name: Option<String>,
    /// User's family/last name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub family_name: Option<String>,
    /// User's email address.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    /// User's phone number.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    /// Verification level achieved.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verification_level: Option<String>,
    /// Timestamp when verified (Unix epoch seconds).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verified_at: Option<i64>,
}

/// Parsed `AgeProof` credential for JavaScript.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(TS))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, ts(export))]
pub struct ParsedAgeProof {
    /// Actual age (if disclosed).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub age: Option<i64>,
    /// Whether age was verified.
    pub age_verified: bool,
    /// Date when verification occurred.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verification_date: Option<String>,
    /// The minimum age threshold that was verified (e.g., 18, 21).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub minimum_age_met: Option<i64>,
}

// =============================================================================
// WASM-only implementations: Convert rich Rust types to boundary types
// =============================================================================

#[cfg(target_arch = "wasm32")]
impl From<crate::types::RequestStatus> for StatusResponse {
    fn from(status: crate::types::RequestStatus) -> Self {
        match status {
            crate::types::RequestStatus::Pending { request_id, .. } => StatusResponse {
                status: "pending".into(),
                request_id: request_id.to_string(),
                presentation: None,
                error: None,
                elapsed_seconds: None,
            },
            crate::types::RequestStatus::Fulfilled {
                request_id,
                presentation,
                ..
            } => StatusResponse {
                status: "fulfilled".into(),
                request_id: request_id.to_string(),
                presentation: Some(presentation),
                error: None,
                elapsed_seconds: None,
            },
            crate::types::RequestStatus::Denied {
                request_id, reason, ..
            } => StatusResponse {
                status: "denied".into(),
                request_id: request_id.to_string(),
                presentation: None,
                error: Some(reason),
                elapsed_seconds: None,
            },
            crate::types::RequestStatus::Expired { request_id, .. } => StatusResponse {
                status: "expired".into(),
                request_id: request_id.to_string(),
                presentation: None,
                error: None,
                elapsed_seconds: None,
            },
            crate::types::RequestStatus::NotFound {
                request_id,
                message,
            } => StatusResponse {
                status: "notfound".into(),
                request_id: request_id.to_string(),
                presentation: None,
                error: Some(message),
                elapsed_seconds: None,
            },
            crate::types::RequestStatus::Timeout {
                request_id,
                elapsed_seconds,
            } => StatusResponse {
                status: "timeout".into(),
                request_id: request_id.to_string(),
                presentation: None,
                error: None,
                elapsed_seconds: Some(elapsed_seconds),
            },
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::DeviceAttestation> for DeviceAttestation {
    fn from(att: crate::types::DeviceAttestation) -> Self {
        Self {
            has_secure_element: att.has_secure_element,
            has_tee: att.has_tee,
            integrity_status: att.integrity_status,
            integrity_indicators: att.integrity_indicators,
            is_emulator: att.is_emulator,
            is_debugged: att.is_debugged,
            bootloader_locked: att.bootloader_locked,
            device_encrypted: att.device_encrypted,
            hardware_fingerprint: att.hardware_fingerprint,
            app_attest_supported: att.app_attest_supported,
            app_attest_key_id: att.app_attest_key_id,
            key_attestation_security_level: att.key_attestation_security_level,
            model: att.model,
            os_version: att.os_version,
            collected_at: att.collected_at,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::DeviceSecurity> for DeviceSecurity {
    fn from(sec: crate::types::DeviceSecurity) -> Self {
        Self {
            security_level: sec.security_level,
            biometric_used: sec.biometric_used,
            platform: sec.platform,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::FieldVerificationMetadata> for FieldVerificationMetadata {
    fn from(meta: crate::types::FieldVerificationMetadata) -> Self {
        Self {
            verified: meta.verified,
            provider: meta.provider,
            method: meta.method,
            verified_at: meta.verified_at,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::VerifiedCredential> for VerifiedCredential {
    fn from(cred: crate::types::VerifiedCredential) -> Self {
        Self {
            subject_did: cred.subject_did,
            issuer_did: cred.issuer_did,
            credential_type: cred.credential_type,
            claims: cred.claims,
            issued_at: cred.issued_at.to_rfc3339(),
            expires_at: cred.expires_at.to_rfc3339(),
            issuer_status: format!("{:?}", cred.issuer_status),
            signature_verified: cred.signature_verified,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::VerifiedResponse> for VerifiedResponse {
    fn from(resp: crate::types::VerifiedResponse) -> Self {
        Self {
            credentials: resp
                .credentials()
                .map(|c| VerifiedCredential::from(c.clone()))
                .collect(),
            device_attestation: resp
                .device_attestation()
                .cloned()
                .map(DeviceAttestation::from),
            device_security: resp.device_security().cloned().map(DeviceSecurity::from),
            field_verification: None, // Raw JSON, will be accessed via field_verification() method
        }
    }
}

// =============================================================================
// Parsed Type Conversions (WASM-only)
// =============================================================================

#[cfg(target_arch = "wasm32")]
impl From<crate::types::parsed::ParsedPersona> for ParsedPersona {
    fn from(p: crate::types::parsed::ParsedPersona) -> Self {
        Self {
            display_name: p.display_name,
            email: p.email,
            phone: p.phone,
            company: p.company,
            job_title: p.job_title,
            avatar_url: p.avatar_url,
            website: p.website,
            location: p.location,
            visibility: p.visibility,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::parsed::ParsedEmployment> for ParsedEmployment {
    fn from(e: crate::types::parsed::ParsedEmployment) -> Self {
        Self {
            company: e.company,
            title: e.title,
            department: e.department,
            start_date: e.start_date,
            end_date: e.end_date,
            is_current: e.is_current,
            responsibilities: e.responsibilities,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::parsed::ParsedVerifiedEmail> for ParsedVerifiedEmail {
    fn from(v: crate::types::parsed::ParsedVerifiedEmail) -> Self {
        Self {
            email: v.email,
            verified_at: v.verified_at,
            verification_method: v.verification_method,
            provider: v.provider,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::parsed::ParsedVerifiedPhone> for ParsedVerifiedPhone {
    fn from(v: crate::types::parsed::ParsedVerifiedPhone) -> Self {
        Self {
            phone_number: v.phone_number,
            verified_at: v.verified_at,
            verification_method: v.verification_method,
            provider: v.provider,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::parsed::ParsedVerifiedPersona> for ParsedVerifiedPersona {
    fn from(v: crate::types::parsed::ParsedVerifiedPersona) -> Self {
        Self {
            display_name: v.display_name,
            given_name: v.given_name,
            family_name: v.family_name,
            email: v.email,
            phone: v.phone,
            verification_level: v.verification_level,
            verified_at: v.verified_at,
        }
    }
}

#[cfg(target_arch = "wasm32")]
impl From<crate::types::parsed::ParsedAgeProof> for ParsedAgeProof {
    fn from(a: crate::types::parsed::ParsedAgeProof) -> Self {
        Self {
            age: a.age,
            age_verified: a.age_verified,
            verification_date: a.verification_date,
            minimum_age_met: a.minimum_age_met,
        }
    }
}
