//! Public API types for Multi-Rail Verification Bus (MRVB)
//!
//! This crate defines the public API boundary between MRVB and its consumers (like UVB).
//! These types form the contract - changes here are breaking changes.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Identifies which verification rail/channel to use
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RailKind {
    /// WebAuthn/FIDO2 (Rail 0) - Cryptographic, phishing-resistant, $0
    WebAuthn,
    /// User texts you - often $0 inbound
    ReverseSms,
    /// SIM/network verification - ~$0.03
    SilentNetworkAuth,
    /// Mobile app push notification - $0
    Push,
    /// WhatsApp Business API - conversation-based pricing
    WhatsAppOtp,
    /// Outbound SMS (toll-free or A2P) - $0.01-0.03/msg
    TollFreeSms,
    /// Voice-based OTP via flash call - ~$0.005-0.01
    FlashCall,
    /// Email OTP via SES or similar - ~$0.0001/email
    EmailOtp,
    /// Local hardware gateway (dev/testing only)
    EmergencyHardwareGateway,
}

/// Why this verification is happening - affects rail selection and risk assessment
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VerificationContext {
    /// User logging in
    Login,
    /// New user registration
    Signup,
    /// Password reset flow
    PasswordReset,
    /// High-value action (e.g., large transfer, account deletion)
    HighValueAction,
    /// Other use case
    Other,
}

/// Status of a verification session
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SessionStatus {
    /// Session created, awaiting verification
    Pending,
    /// Successfully verified
    Verified,
    /// Verification failed (wrong code, timeout, etc.)
    Failed,
    /// Session expired
    Expired,
    /// Session cancelled by user or system
    Cancelled,
}

/// Request to start a verification session
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationRequest {
    /// Tenant/customer identifier
    pub tenant_id: String,
    /// User identifier
    pub user_id: String,
    /// Contact point (email, phone, etc.)
    pub contact: String,
    /// Why this verification is needed
    pub context: VerificationContext,
    /// Preferred rails (in priority order), or None for auto-selection
    pub preferred_rails: Option<Vec<RailKind>>,
    /// Maximum cost in micro-USD (1,000,000 = $1), or None for tenant default
    pub max_cost_micro_usd: Option<u64>,
    /// Additional metadata
    #[serde(default)]
    pub metadata: serde_json::Value,
}

/// Response from starting a verification session
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationResponse {
    /// Unique session ID
    pub session_id: Uuid,
    /// Rails that will be attempted (in order)
    pub rails: Vec<RailKind>,
    /// Estimated cost in micro-USD
    pub estimated_cost_micro_usd: u64,
    /// Session status
    pub status: SessionStatus,
    /// When the session was created
    pub created_at: DateTime<Utc>,
    /// When the session expires
    pub expires_at: DateTime<Utc>,
}

/// Request to verify a challenge (OTP code, etc.)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyRequest {
    /// Session ID to verify
    pub session_id: Uuid,
    /// The challenge response (e.g., OTP code)
    pub challenge: String,
}

/// Result of verification attempt
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyResponse {
    /// Session ID
    pub session_id: Uuid,
    /// Whether verification succeeded
    pub verified: bool,
    /// Current session status
    pub status: SessionStatus,
    /// Actual cost in micro-USD
    pub actual_cost_micro_usd: u64,
    /// Number of attempts remaining (if applicable)
    pub attempts_remaining: Option<u32>,
}

/// Cost information for a rail/provider combination
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostInfo {
    /// Rail type
    pub rail: RailKind,
    /// Provider name
    pub provider: String,
    /// Estimated cost in micro-USD
    pub estimated_cost_micro_usd: u64,
    /// Actual cost in micro-USD (if known)
    pub actual_cost_micro_usd: Option<u64>,
}

impl RailKind {
    /// Returns the typical cost range for this rail in micro-USD
    pub fn typical_cost_range_micro_usd(&self) -> (u64, u64) {
        match self {
            RailKind::WebAuthn => (0, 0),
            RailKind::ReverseSms => (0, 0),
            RailKind::Push => (0, 0),
            RailKind::EmailOtp => (100, 500), // $0.0001-0.0005
            RailKind::FlashCall => (5_000, 10_000), // $0.005-0.01
            RailKind::SilentNetworkAuth => (30_000, 30_000), // ~$0.03
            RailKind::TollFreeSms => (10_000, 30_000), // $0.01-0.03
            RailKind::WhatsAppOtp => (5_000, 20_000), // Conversation-based
            RailKind::EmergencyHardwareGateway => (0, 0),
        }
    }

    /// Returns whether this rail requires user action (true) or is passive (false)
    pub fn requires_user_action(&self) -> bool {
        match self {
            RailKind::WebAuthn => true,
            RailKind::ReverseSms => true,
            RailKind::Push => true,
            RailKind::EmailOtp => true,
            RailKind::TollFreeSms => true,
            RailKind::FlashCall => true,
            RailKind::WhatsAppOtp => true,
            RailKind::SilentNetworkAuth => false, // Passive SIM verification
            RailKind::EmergencyHardwareGateway => true,
        }
    }
}

impl VerificationContext {
    /// Returns the recommended security level (0-10) for this context
    pub fn security_level(&self) -> u8 {
        match self {
            VerificationContext::Login => 5,
            VerificationContext::Signup => 3,
            VerificationContext::PasswordReset => 7,
            VerificationContext::HighValueAction => 10,
            VerificationContext::Other => 5,
        }
    }
}

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

    #[test]
    fn test_rail_cost_ranges() {
        assert_eq!(RailKind::WebAuthn.typical_cost_range_micro_usd(), (0, 0));
        assert_eq!(
            RailKind::EmailOtp.typical_cost_range_micro_usd(),
            (100, 500)
        );
        assert!(RailKind::TollFreeSms.typical_cost_range_micro_usd().1 > 0);
    }

    #[test]
    fn test_rail_user_action() {
        assert!(RailKind::EmailOtp.requires_user_action());
        assert!(!RailKind::SilentNetworkAuth.requires_user_action());
    }

    #[test]
    fn test_context_security_levels() {
        assert!(
            VerificationContext::HighValueAction.security_level()
                > VerificationContext::Signup.security_level()
        );
    }

    #[test]
    fn test_verification_request_serialization() {
        let req = VerificationRequest {
            tenant_id: "tenant123".to_string(),
            user_id: "user456".to_string(),
            contact: "user@example.com".to_string(),
            context: VerificationContext::Login,
            preferred_rails: Some(vec![RailKind::EmailOtp]),
            max_cost_micro_usd: Some(1000),
            metadata: serde_json::json!({"source": "web"}),
        };

        let json = serde_json::to_string(&req).unwrap();
        let deserialized: VerificationRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.tenant_id, "tenant123");
    }
}
