use argon2::PasswordVerifier;
use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{delete, get, post},
    Json, Router,
};
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use chrono::{DateTime, Duration, Utc};
use diesel::prelude::*;
use diesel::sql_types::{BigInt, Bytea, Nullable, Text, Timestamptz, Uuid as DieselUuid};
use log::{error, info, warn};
use ring::rand::{SecureRandom, SystemRandom};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;

use crate::shared::state::AppState;
use crate::shared::utils::DbPool;

const CHALLENGE_TIMEOUT_SECONDS: i64 = 300;
const PASSKEY_NAME_MAX_LENGTH: usize = 64;

#[derive(Debug, Clone)]
struct FallbackAttemptTracker {
    attempts: u32,
    locked_until: Option<DateTime<Utc>>,
}

pub struct PasskeyCredential {
    pub id: String,
    pub user_id: Uuid,
    pub credential_id: Vec<u8>,
    pub public_key: Vec<u8>,
    pub counter: u32,
    pub name: String,
    pub created_at: DateTime<Utc>,
    pub last_used_at: Option<DateTime<Utc>>,
    pub aaguid: Option<Vec<u8>>,
    pub transports: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasskeyChallenge {
    pub challenge: Vec<u8>,
    pub user_id: Option<Uuid>,
    pub created_at: DateTime<Utc>,
    pub operation: ChallengeOperation,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChallengeOperation {
    Registration,
    Authentication,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegistrationOptionsRequest {
    pub user_id: Uuid,
    pub username: String,
    pub display_name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegistrationOptions {
    pub challenge: String,
    pub rp: RelyingParty,
    pub user: UserEntity,
    pub pub_key_cred_params: Vec<PubKeyCredParam>,
    pub timeout: u32,
    pub attestation: String,
    pub authenticator_selection: AuthenticatorSelection,
    pub exclude_credentials: Vec<CredentialDescriptor>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelyingParty {
    pub id: String,
    pub name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserEntity {
    pub id: String,
    pub name: String,
    pub display_name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PubKeyCredParam {
    #[serde(rename = "type")]
    pub cred_type: String,
    pub alg: i32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticatorSelection {
    pub authenticator_attachment: Option<String>,
    pub resident_key: String,
    pub require_resident_key: bool,
    pub user_verification: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CredentialDescriptor {
    #[serde(rename = "type")]
    pub cred_type: String,
    pub id: String,
    pub transports: Option<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegistrationResponse {
    pub id: String,
    pub raw_id: String,
    pub response: AuthenticatorAttestationResponse,
    #[serde(rename = "type")]
    pub cred_type: String,
    pub client_extension_results: Option<HashMap<String, serde_json::Value>>,
    pub authenticator_attachment: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticatorAttestationResponse {
    pub client_data_json: String,
    pub attestation_object: String,
    pub transports: Option<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticationOptionsRequest {
    pub username: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticationOptions {
    pub challenge: String,
    pub timeout: u32,
    pub rp_id: String,
    pub allow_credentials: Vec<CredentialDescriptor>,
    pub user_verification: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticationResponse {
    pub id: String,
    pub raw_id: String,
    pub response: AuthenticatorAssertionResponse,
    #[serde(rename = "type")]
    pub cred_type: String,
    pub client_extension_results: Option<HashMap<String, serde_json::Value>>,
    pub authenticator_attachment: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticatorAssertionResponse {
    pub client_data_json: String,
    pub authenticator_data: String,
    pub signature: String,
    pub user_handle: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasskeyInfo {
    pub id: String,
    pub name: String,
    pub created_at: DateTime<Utc>,
    pub last_used_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RenamePasskeyRequest {
    pub name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationResult {
    pub success: bool,
    pub user_id: Option<Uuid>,
    pub credential_id: Option<String>,
    pub error: Option<String>,
    pub used_fallback: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegistrationResult {
    pub success: bool,
    pub credential_id: Option<String>,
    pub error: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasswordFallbackRequest {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasswordFallbackResponse {
    pub success: bool,
    pub user_id: Option<Uuid>,
    pub token: Option<String>,
    pub error: Option<String>,
    pub passkey_available: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FallbackConfig {
    pub enabled: bool,
    pub require_additional_verification: bool,
    pub max_fallback_attempts: u32,
    pub lockout_duration_seconds: u64,
    pub prompt_passkey_setup: bool,
}

impl Default for FallbackConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            require_additional_verification: false,
            max_fallback_attempts: 5,
            lockout_duration_seconds: 900, // 15 minutes
            prompt_passkey_setup: true,
        }
    }
}

#[derive(QueryableByName)]
struct PasskeyRow {
    #[diesel(sql_type = Text)]
    id: String,
    #[diesel(sql_type = DieselUuid)]
    user_id: Uuid,
    #[diesel(sql_type = Bytea)]
    credential_id: Vec<u8>,
    #[diesel(sql_type = Bytea)]
    public_key: Vec<u8>,
    #[diesel(sql_type = BigInt)]
    counter: i64,
    #[diesel(sql_type = Text)]
    name: String,
    #[diesel(sql_type = Timestamptz)]
    created_at: DateTime<Utc>,
    #[diesel(sql_type = Nullable<Timestamptz>)]
    last_used_at: Option<DateTime<Utc>>,
    #[diesel(sql_type = Nullable<Bytea>)]
    aaguid: Option<Vec<u8>>,
    #[diesel(sql_type = Nullable<Text>)]
    transports: Option<String>,
}

pub struct PasskeyService {
    pool: Arc<diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>>,
    rp_id: String,
    rp_name: String,
    rp_origin: String,
    challenges: Arc<RwLock<HashMap<String, PasskeyChallenge>>>,
    rng: SystemRandom,
    fallback_config: FallbackConfig,
    fallback_attempts: Arc<RwLock<HashMap<String, FallbackAttemptTracker>>>,
}

pub struct StorePasskeyParams<'a> {
    pub user_id: Uuid,
    pub credential_id: &'a [u8],
    pub public_key: &'a [u8],
    pub counter: u32,
    pub name: &'a str,
    pub aaguid: Option<&'a [u8]>,
    pub transports: &'a str,
}

impl PasskeyService {
    pub fn new(
        pool: DbPool,
        rp_id: String,
        rp_name: String,
        rp_origin: String,
    ) -> Self {
        Self {
            pool: Arc::new(pool),
            rp_id,
            rp_name,
            rp_origin,
            challenges: Arc::new(RwLock::new(HashMap::new())),
            rng: SystemRandom::new(),
            fallback_config: FallbackConfig::default(),
            fallback_attempts: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub fn with_fallback_config(
        pool: DbPool,
        rp_id: String,
        rp_name: String,
        rp_origin: String,
        fallback_config: FallbackConfig,
    ) -> Self {
        Self {
            pool: Arc::new(pool),
            rp_id,
            rp_name,
            rp_origin,
            challenges: Arc::new(RwLock::new(HashMap::new())),
            rng: SystemRandom::new(),
            fallback_config,
            fallback_attempts: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub fn user_has_passkeys(&self, username: &str) -> Result<bool, PasskeyError> {
        let passkeys = self.get_passkeys_by_username(username)?;
        Ok(!passkeys.is_empty())
    }

    pub async fn authenticate_with_password_fallback(
        &self,
        request: &PasswordFallbackRequest,
    ) -> Result<PasswordFallbackResponse, PasskeyError> {
        if !self.fallback_config.enabled {
            return Ok(PasswordFallbackResponse {
                success: false,
                user_id: None,
                token: None,
                error: Some("Password fallback is disabled".to_string()),
                passkey_available: false,
            });
        }

        // Check if user is locked out
        if self.is_user_locked_out(&request.username).await {
            return Ok(PasswordFallbackResponse {
                success: false,
                user_id: None,
                token: None,
                error: Some("Account temporarily locked due to too many failed attempts".to_string()),
                passkey_available: false,
            });
        }

        // Verify password against database
        let verification_result = self.verify_password(&request.username, &request.password).await;

        match verification_result {
            Ok(user_id) => {
                // Clear failed attempts on successful login
                self.clear_fallback_attempts(&request.username).await;

                // Check if user has passkeys available
                let passkey_available = self.user_has_passkeys(&request.username).unwrap_or(false);

                // Generate session token
                let token = self.generate_session_token(&user_id);

                Ok(PasswordFallbackResponse {
                    success: true,
                    user_id: Some(user_id),
                    token: Some(token),
                    error: None,
                    passkey_available,
                })
            }
            Err(e) => {
                // Track failed attempt
                self.track_fallback_attempt(&request.username).await;

                Ok(PasswordFallbackResponse {
                    success: false,
                    user_id: None,
                    token: None,
                    error: Some(e.to_string()),
                    passkey_available: false,
                })
            }
        }
    }

    async fn is_user_locked_out(&self, username: &str) -> bool {
        let attempts = self.fallback_attempts.read().await;
        if let Some(tracker) = attempts.get(username) {
            if let Some(locked_until) = tracker.locked_until {
                return Utc::now() < locked_until;
            }
        }
        false
    }

    async fn track_fallback_attempt(&self, username: &str) {
        let mut attempts = self.fallback_attempts.write().await;
        let now = Utc::now();

        let tracker = attempts.entry(username.to_string()).or_insert(FallbackAttemptTracker {
            attempts: 0,
            locked_until: None,
        });

        tracker.attempts += 1;

        // Check if we should lock out the user
        if tracker.attempts >= self.fallback_config.max_fallback_attempts {
            tracker.locked_until = Some(
                now + chrono::Duration::seconds(self.fallback_config.lockout_duration_seconds as i64)
            );
        }
    }

    async fn clear_fallback_attempts(&self, username: &str) {
        let mut attempts = self.fallback_attempts.write().await;
        attempts.remove(username);
    }

    async fn verify_password(&self, username: &str, password: &str) -> Result<Uuid, PasskeyError> {
        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        #[derive(QueryableByName)]
        struct UserPasswordRow {
            #[diesel(sql_type = DieselUuid)]
            id: Uuid,
            #[diesel(sql_type = Nullable<Text>)]
            password_hash: Option<String>,
        }

        let result: Option<UserPasswordRow> = diesel::sql_query(
            "SELECT id, password_hash FROM users WHERE username = $1 OR email = $1"
        )
        .bind::<Text, _>(username)
        .get_result::<UserPasswordRow>(&mut conn)
        .optional()
        .map_err(|_| PasskeyError::DatabaseError)?;

        match result {
            Some(row) => {
                if let Some(hash) = row.password_hash {
                    let parsed_hash = argon2::PasswordHash::new(&hash)
                        .map_err(|_| PasskeyError::InvalidCredentialId)?;

                    if argon2::Argon2::default()
                        .verify_password(password.as_bytes(), &parsed_hash)
                        .is_ok()
                    {
                        return Ok(row.id);
                    }
                }
                Err(PasskeyError::InvalidCredentialId)
            }
            None => Err(PasskeyError::InvalidCredentialId),
        }
    }

    fn generate_session_token(&self, user_id: &Uuid) -> String {
        let random_bytes: [u8; 32] = rand::random();
        let token = base64::Engine::encode(
            &base64::engine::general_purpose::URL_SAFE_NO_PAD,
            random_bytes
        );
        format!("{}:{}", user_id, token)
    }

    pub fn should_offer_password_fallback(&self, username: &str) -> Result<bool, PasskeyError> {
        if !self.fallback_config.enabled {
            return Ok(false);
        }

        let has_passkeys = self.user_has_passkeys(username)?;
        Ok(!has_passkeys || self.fallback_config.enabled)
    }

    pub fn get_fallback_config(&self) -> &FallbackConfig {
        &self.fallback_config
    }

    pub fn set_fallback_config(&mut self, config: FallbackConfig) {
        self.fallback_config = config;
    }

    pub async fn generate_registration_options(
        &self,
        request: RegistrationOptionsRequest,
    ) -> Result<RegistrationOptions, PasskeyError> {
        let challenge = self.generate_challenge()?;
        let challenge_b64 = URL_SAFE_NO_PAD.encode(&challenge);

        let passkey_challenge = PasskeyChallenge {
            challenge: challenge.clone(),
            user_id: Some(request.user_id),
            created_at: Utc::now(),
            operation: ChallengeOperation::Registration,
        };

        {
            let mut challenges = self.challenges.write().await;
            challenges.insert(challenge_b64.clone(), passkey_challenge);
        }

        let existing_credentials = self.get_user_passkeys(request.user_id)?;
        let exclude_credentials: Vec<CredentialDescriptor> = existing_credentials
            .into_iter()
            .map(|pk| CredentialDescriptor {
                cred_type: "public-key".to_string(),
                id: URL_SAFE_NO_PAD.encode(&pk.credential_id),
                transports: Some(pk.transports),
            })
            .collect();

        let user_id_b64 = URL_SAFE_NO_PAD.encode(request.user_id.as_bytes());

        Ok(RegistrationOptions {
            challenge: challenge_b64,
            rp: RelyingParty {
                id: self.rp_id.clone(),
                name: self.rp_name.clone(),
            },
            user: UserEntity {
                id: user_id_b64,
                name: request.username,
                display_name: request.display_name,
            },
            pub_key_cred_params: vec![
                PubKeyCredParam {
                    cred_type: "public-key".to_string(),
                    alg: -7,
                },
                PubKeyCredParam {
                    cred_type: "public-key".to_string(),
                    alg: -257,
                },
            ],
            timeout: 60000,
            attestation: "none".to_string(),
            authenticator_selection: AuthenticatorSelection {
                authenticator_attachment: None,
                resident_key: "preferred".to_string(),
                require_resident_key: false,
                user_verification: "preferred".to_string(),
            },
            exclude_credentials,
        })
    }

    pub async fn verify_registration(
        &self,
        response: RegistrationResponse,
        passkey_name: Option<String>,
    ) -> Result<RegistrationResult, PasskeyError> {
        let client_data_json = URL_SAFE_NO_PAD
            .decode(&response.response.client_data_json)
            .map_err(|_| PasskeyError::InvalidClientData)?;

        let client_data: ClientData = serde_json::from_slice(&client_data_json)
            .map_err(|_| PasskeyError::InvalidClientData)?;

        if client_data.r#type != "webauthn.create" {
            return Err(PasskeyError::InvalidCeremonyType);
        }

        if !self.verify_origin(&client_data.origin) {
            return Err(PasskeyError::InvalidOrigin);
        }

        let challenge_bytes = URL_SAFE_NO_PAD
            .decode(&client_data.challenge)
            .map_err(|_| PasskeyError::InvalidChallenge)?;
        log::debug!("Decoded challenge bytes, length: {}", challenge_bytes.len());

        let stored_challenge = self.get_and_remove_challenge(&client_data.challenge).await?;

        if stored_challenge.operation != ChallengeOperation::Registration {
            return Err(PasskeyError::InvalidCeremonyType);
        }

        let user_id = stored_challenge.user_id.ok_or(PasskeyError::MissingUserId)?;

        let attestation_object = URL_SAFE_NO_PAD
            .decode(&response.response.attestation_object)
            .map_err(|_| PasskeyError::InvalidAttestationObject)?;

        let (auth_data, public_key, aaguid) = self.parse_attestation_object(&attestation_object)?;
        log::debug!("Parsed attestation object, auth_data length: {}", auth_data.len());

        let credential_id = URL_SAFE_NO_PAD
            .decode(&response.raw_id)
            .map_err(|_| PasskeyError::InvalidCredentialId)?;

        let name = passkey_name.unwrap_or_else(|| {
            format!("Passkey {}", Utc::now().format("%Y-%m-%d %H:%M"))
        });

        let sanitized_name: String = name
            .chars()
            .filter(|c| c.is_alphanumeric() || c.is_whitespace() || *c == '-' || *c == '_')
            .take(PASSKEY_NAME_MAX_LENGTH)
            .collect();

        let transports = response
            .response
            .transports
            .unwrap_or_default()
            .join(",");

        self.store_passkey(StorePasskeyParams {
            user_id,
            credential_id: &credential_id,
            public_key: &public_key,
            counter: 0,
            name: &sanitized_name,
            aaguid: aaguid.as_deref(),
            transports: &transports,
        })?;

        info!("Passkey registered for user {}", user_id);

        Ok(RegistrationResult {
            success: true,
            credential_id: Some(URL_SAFE_NO_PAD.encode(&credential_id)),
            error: None,
        })
    }

    pub async fn generate_authentication_options(
        &self,
        request: AuthenticationOptionsRequest,
    ) -> Result<AuthenticationOptions, PasskeyError> {
        let challenge = self.generate_challenge()?;
        let challenge_b64 = URL_SAFE_NO_PAD.encode(&challenge);

        let passkey_challenge = PasskeyChallenge {
            challenge: challenge.clone(),
            user_id: None,
            created_at: Utc::now(),
            operation: ChallengeOperation::Authentication,
        };

        {
            let mut challenges = self.challenges.write().await;
            challenges.insert(challenge_b64.clone(), passkey_challenge);
        }

        let allow_credentials = if let Some(username) = request.username {
            let credentials = self.get_passkeys_by_username(&username)?;
            credentials
                .into_iter()
                .map(|pk| CredentialDescriptor {
                    cred_type: "public-key".to_string(),
                    id: URL_SAFE_NO_PAD.encode(&pk.credential_id),
                    transports: Some(pk.transports),
                })
                .collect()
        } else {
            Vec::new()
        };

        Ok(AuthenticationOptions {
            challenge: challenge_b64,
            timeout: 60000,
            rp_id: self.rp_id.clone(),
            allow_credentials,
            user_verification: "preferred".to_string(),
        })
    }

    pub async fn verify_authentication(
        &self,
        response: AuthenticationResponse,
    ) -> Result<VerificationResult, PasskeyError> {
        let client_data_json = URL_SAFE_NO_PAD
            .decode(&response.response.client_data_json)
            .map_err(|_| PasskeyError::InvalidClientData)?;

        let client_data: ClientData = serde_json::from_slice(&client_data_json)
            .map_err(|_| PasskeyError::InvalidClientData)?;

        if client_data.r#type != "webauthn.get" {
            return Err(PasskeyError::InvalidCeremonyType);
        }

        if !self.verify_origin(&client_data.origin) {
            return Err(PasskeyError::InvalidOrigin);
        }

        let _stored_challenge = self.get_and_remove_challenge(&client_data.challenge).await?;

        let credential_id = URL_SAFE_NO_PAD
            .decode(&response.raw_id)
            .map_err(|_| PasskeyError::InvalidCredentialId)?;

        let passkey = self.get_passkey_by_credential_id(&credential_id)?;

        let authenticator_data = URL_SAFE_NO_PAD
            .decode(&response.response.authenticator_data)
            .map_err(|_| PasskeyError::InvalidAuthenticatorData)?;

        let signature = URL_SAFE_NO_PAD
            .decode(&response.response.signature)
            .map_err(|_| PasskeyError::InvalidSignature)?;

        let rp_id_hash = Sha256::digest(self.rp_id.as_bytes());
        if authenticator_data.len() < 37 || &authenticator_data[..32] != rp_id_hash.as_slice() {
            return Err(PasskeyError::RpIdMismatch);
        }

        let flags = authenticator_data[32];
        let user_present = (flags & 0x01) != 0;
        if !user_present {
            return Err(PasskeyError::UserNotPresent);
        }

        let counter_bytes: [u8; 4] = authenticator_data[33..37]
            .try_into()
            .map_err(|_| PasskeyError::InvalidAuthenticatorData)?;
        let counter = u32::from_be_bytes(counter_bytes);

        if counter > 0 && counter <= passkey.counter {
            warn!(
                "Possible credential cloning detected for user {}",
                passkey.user_id
            );
            return Err(PasskeyError::CounterMismatch);
        }

        let mut verification_data = Vec::new();
        verification_data.extend_from_slice(&authenticator_data);
        verification_data.extend_from_slice(&Sha256::digest(&client_data_json));

        let signature_valid = self.verify_signature(
            &passkey.public_key,
            &verification_data,
            &signature,
        )?;

        if !signature_valid {
            return Err(PasskeyError::SignatureVerificationFailed);
        }

        self.update_passkey_counter(&credential_id, counter)?;

        info!("Passkey authentication successful for user {}", passkey.user_id);

        Ok(VerificationResult {
            success: true,
            user_id: Some(passkey.user_id),
            credential_id: Some(URL_SAFE_NO_PAD.encode(&credential_id)),
            error: None,
            used_fallback: false,
        })
    }

    pub fn get_user_passkeys(&self, user_id: Uuid) -> Result<Vec<PasskeyCredential>, PasskeyError> {
        let mut conn = self.pool.get().map_err(|e| {
            error!("Failed to get database connection: {e}");
            PasskeyError::DatabaseError
        })?;

        let rows: Vec<PasskeyRow> = diesel::sql_query(
            "SELECT id, user_id, credential_id, public_key, counter, name, created_at, last_used_at, aaguid, transports FROM passkeys WHERE user_id = $1 ORDER BY created_at DESC"
        )
            .bind::<DieselUuid, _>(user_id)
            .load(&mut conn)
            .map_err(|e| {
                error!("Failed to query passkeys: {e}");
                PasskeyError::DatabaseError
            })?;

        let credentials = rows
            .into_iter()
            .map(|row| PasskeyCredential {
                id: row.id,
                user_id: row.user_id,
                credential_id: row.credential_id,
                public_key: row.public_key,
                counter: row.counter as u32,
                name: row.name,
                created_at: row.created_at,
                last_used_at: row.last_used_at,
                aaguid: row.aaguid,
                transports: row
                    .transports
                    .map(|t| t.split(',').map(String::from).collect())
                    .unwrap_or_default(),
            })
            .collect();

        Ok(credentials)
    }

    pub fn list_passkeys(&self, user_id: Uuid) -> Result<Vec<PasskeyInfo>, PasskeyError> {
        let passkeys = self.get_user_passkeys(user_id)?;
        let info = passkeys
            .into_iter()
            .map(|pk| PasskeyInfo {
                id: pk.id,
                name: pk.name,
                created_at: pk.created_at,
                last_used_at: pk.last_used_at,
            })
            .collect();
        Ok(info)
    }

    pub fn rename_passkey(
        &self,
        user_id: Uuid,
        passkey_id: &str,
        new_name: &str,
    ) -> Result<(), PasskeyError> {
        let sanitized_name: String = new_name
            .chars()
            .filter(|c| c.is_alphanumeric() || c.is_whitespace() || *c == '-' || *c == '_')
            .take(PASSKEY_NAME_MAX_LENGTH)
            .collect();

        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        let result = diesel::sql_query(
            "UPDATE passkeys SET name = $1 WHERE id = $2 AND user_id = $3",
        )
        .bind::<Text, _>(&sanitized_name)
        .bind::<Text, _>(passkey_id)
        .bind::<DieselUuid, _>(user_id)
        .execute(&mut conn)
        .map_err(|e| {
            error!("Failed to rename passkey: {e}");
            PasskeyError::DatabaseError
        })?;

        if result == 0 {
            return Err(PasskeyError::PasskeyNotFound);
        }

        Ok(())
    }

    pub fn delete_passkey(&self, user_id: Uuid, passkey_id: &str) -> Result<(), PasskeyError> {
        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        let result = diesel::sql_query(
            "DELETE FROM passkeys WHERE id = $1 AND user_id = $2",
        )
        .bind::<Text, _>(passkey_id)
        .bind::<DieselUuid, _>(user_id)
        .execute(&mut conn)
        .map_err(|e| {
            error!("Failed to delete passkey: {e}");
            PasskeyError::DatabaseError
        })?;

        if result == 0 {
            return Err(PasskeyError::PasskeyNotFound);
        }

        info!("Passkey {} deleted for user {}", passkey_id, user_id);
        Ok(())
    }

    fn generate_challenge(&self) -> Result<Vec<u8>, PasskeyError> {
        let mut challenge = vec![0u8; 32];
        self.rng
            .fill(&mut challenge)
            .map_err(|_| PasskeyError::ChallengeGenerationFailed)?;
        Ok(challenge)
    }

    async fn get_and_remove_challenge(&self, challenge_b64: &str) -> Result<PasskeyChallenge, PasskeyError> {
        let mut challenges = self.challenges.write().await;

        let challenge = challenges
            .remove(challenge_b64)
            .ok_or(PasskeyError::ChallengeNotFound)?;

        let age = Utc::now() - challenge.created_at;
        if age.num_seconds() > CHALLENGE_TIMEOUT_SECONDS {
            return Err(PasskeyError::ChallengeExpired);
        }

        Ok(challenge)
    }

    fn verify_origin(&self, origin: &str) -> bool {
        origin == self.rp_origin
    }

    fn parse_attestation_object(
        &self,
        attestation_object: &[u8],
    ) -> Result<(Vec<u8>, Vec<u8>, Option<Vec<u8>>), PasskeyError> {
        let value: ciborium::Value = ciborium::from_reader(attestation_object)
            .map_err(|_| PasskeyError::InvalidAttestationObject)?;

        let map = value
            .as_map()
            .ok_or(PasskeyError::InvalidAttestationObject)?;

        let auth_data = map
            .iter()
            .find(|(k, _)| k.as_text() == Some("authData"))
            .and_then(|(_, v)| v.as_bytes())
            .ok_or(PasskeyError::InvalidAttestationObject)?
            .to_vec();

        if auth_data.len() < 37 {
            return Err(PasskeyError::InvalidAuthenticatorData);
        }

        let rp_id_hash = Sha256::digest(self.rp_id.as_bytes());
        if &auth_data[..32] != rp_id_hash.as_slice() {
            return Err(PasskeyError::RpIdMismatch);
        }

        let flags = auth_data[32];
        let has_attested_credential = (flags & 0x40) != 0;

        if !has_attested_credential {
            return Err(PasskeyError::NoAttestedCredential);
        }

        let aaguid = auth_data[37..53].to_vec();

        let cred_id_len = u16::from_be_bytes([auth_data[53], auth_data[54]]) as usize;
        let cred_id_end = 55 + cred_id_len;

        if auth_data.len() < cred_id_end {
            return Err(PasskeyError::InvalidAuthenticatorData);
        }

        let public_key_cbor = &auth_data[cred_id_end..];
        let public_key = public_key_cbor.to_vec();

        Ok((auth_data, public_key, Some(aaguid)))
    }

    fn verify_signature(
        &self,
        public_key_cbor: &[u8],
        data: &[u8],
        signature: &[u8],
    ) -> Result<bool, PasskeyError> {
        let pk_value: ciborium::Value = ciborium::from_reader(public_key_cbor)
            .map_err(|_| PasskeyError::InvalidPublicKey)?;

        let pk_map = pk_value
            .as_map()
            .ok_or(PasskeyError::InvalidPublicKey)?;

        let kty = pk_map
            .iter()
            .find(|(k, _)| k.as_integer() == Some(1.into()))
            .and_then(|(_, v)| v.as_integer())
            .ok_or(PasskeyError::InvalidPublicKey)?;

        let alg = pk_map
            .iter()
            .find(|(k, _)| k.as_integer() == Some(3.into()))
            .and_then(|(_, v)| v.as_integer())
            .ok_or(PasskeyError::InvalidPublicKey)?;

        match (i128::from(kty), i128::from(alg)) {
            (2, -7) => self.verify_es256_signature(pk_map, data, signature),
            (3, -257) => self.verify_rs256_signature(pk_map, data, signature),
            _ => Err(PasskeyError::UnsupportedAlgorithm),
        }
    }

    fn verify_es256_signature(
        &self,
        pk_map: &[(ciborium::Value, ciborium::Value)],
        data: &[u8],
        signature: &[u8],
    ) -> Result<bool, PasskeyError> {
        let x = pk_map
            .iter()
            .find(|(k, _)| k.as_integer() == Some((-2).into()))
            .and_then(|(_, v)| v.as_bytes())
            .ok_or(PasskeyError::InvalidPublicKey)?;

        let y = pk_map
            .iter()
            .find(|(k, _)| k.as_integer() == Some((-3).into()))
            .and_then(|(_, v)| v.as_bytes())
            .ok_or(PasskeyError::InvalidPublicKey)?;

        if x.len() != 32 || y.len() != 32 {
            return Err(PasskeyError::InvalidPublicKey);
        }

        let mut public_key_bytes = vec![0x04];
        public_key_bytes.extend_from_slice(x);
        public_key_bytes.extend_from_slice(y);

        let public_key = ring::signature::UnparsedPublicKey::new(
            &ring::signature::ECDSA_P256_SHA256_ASN1,
            &public_key_bytes,
        );

        match public_key.verify(data, signature) {
            Ok(()) => Ok(true),
            Err(_) => Ok(false),
        }
    }

    fn verify_rs256_signature(
        &self,
        pk_map: &[(ciborium::Value, ciborium::Value)],
        data: &[u8],
        signature: &[u8],
    ) -> Result<bool, PasskeyError> {
        let n = pk_map
            .iter()
            .find(|(k, _)| k.as_integer() == Some((-1).into()))
            .and_then(|(_, v)| v.as_bytes())
            .ok_or(PasskeyError::InvalidPublicKey)?;

        let e = pk_map
            .iter()
            .find(|(k, _)| k.as_integer() == Some((-2).into()))
            .and_then(|(_, v)| v.as_bytes())
            .ok_or(PasskeyError::InvalidPublicKey)?;

        let public_key = ring::signature::RsaPublicKeyComponents { n, e };

        match public_key.verify(
            &ring::signature::RSA_PKCS1_2048_8192_SHA256,
            data,
            signature,
        ) {
            Ok(()) => Ok(true),
            Err(_) => Ok(false),
        }
    }



    fn store_passkey(&self, params: StorePasskeyParams<'_>) -> Result<(), PasskeyError> {
        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        let id = Uuid::new_v4().to_string();

        diesel::sql_query(
            r#"
            INSERT INTO passkeys (id, user_id, credential_id, public_key, counter, name, aaguid, transports, created_at)
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW())
            "#,
        )
        .bind::<Text, _>(&id)
        .bind::<DieselUuid, _>(params.user_id)
        .bind::<Bytea, _>(params.credential_id)
        .bind::<Bytea, _>(params.public_key)
        .bind::<BigInt, _>(params.counter as i64)
        .bind::<Text, _>(params.name)
        .bind::<Nullable<Bytea>, _>(params.aaguid)
        .bind::<Text, _>(params.transports)
        .execute(&mut conn)
        .map_err(|e| {
            error!("Failed to store passkey: {e}");
            PasskeyError::DatabaseError
        })?;

        Ok(())
    }

    fn get_passkey_by_credential_id(
        &self,
        credential_id: &[u8],
    ) -> Result<PasskeyCredential, PasskeyError> {
        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        let rows: Vec<PasskeyRow> = diesel::sql_query(
            "SELECT id, user_id, credential_id, public_key, counter, name, created_at, last_used_at, aaguid, transports FROM passkeys WHERE credential_id = $1",
        )
        .bind::<Bytea, _>(credential_id)
        .load(&mut conn)
        .map_err(|e| {
            error!("Failed to query passkey: {e}");
            PasskeyError::DatabaseError
        })?;

        let row = rows.into_iter().next().ok_or(PasskeyError::PasskeyNotFound)?;

        Ok(PasskeyCredential {
            id: row.id,
            user_id: row.user_id,
            credential_id: row.credential_id,
            public_key: row.public_key,
            counter: row.counter as u32,
            name: row.name,
            created_at: row.created_at,
            last_used_at: row.last_used_at,
            aaguid: row.aaguid,
            transports: row
                .transports
                .map(|t| t.split(',').map(String::from).collect())
                .unwrap_or_default(),
        })
    }

    fn get_passkeys_by_username(
        &self,
        username: &str,
    ) -> Result<Vec<PasskeyCredential>, PasskeyError> {
        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        let rows: Vec<PasskeyRow> = diesel::sql_query(
            r#"
            SELECT p.id, p.user_id, p.credential_id, p.public_key, p.counter, p.name, p.created_at, p.last_used_at, p.aaguid, p.transports
            FROM passkeys p
            JOIN users u ON u.id = p.user_id
            WHERE u.username = $1 OR u.email = $1
            ORDER BY p.created_at DESC
            "#,
        )
        .bind::<Text, _>(username)
        .load(&mut conn)
        .map_err(|e| {
            error!("Failed to query passkeys by username: {e}");
            PasskeyError::DatabaseError
        })?;

        let credentials = rows
            .into_iter()
            .map(|row| PasskeyCredential {
                id: row.id,
                user_id: row.user_id,
                credential_id: row.credential_id,
                public_key: row.public_key,
                counter: row.counter as u32,
                name: row.name,
                created_at: row.created_at,
                last_used_at: row.last_used_at,
                aaguid: row.aaguid,
                transports: row
                    .transports
                    .map(|t| t.split(',').map(String::from).collect())
                    .unwrap_or_default(),
            })
            .collect();

        Ok(credentials)
    }

    fn update_passkey_counter(
        &self,
        credential_id: &[u8],
        new_counter: u32,
    ) -> Result<(), PasskeyError> {
        let mut conn = self.pool.get().map_err(|_| PasskeyError::DatabaseError)?;

        diesel::sql_query(
            "UPDATE passkeys SET counter = $1, last_used_at = NOW() WHERE credential_id = $2",
        )
        .bind::<BigInt, _>(new_counter as i64)
        .bind::<Bytea, _>(credential_id)
        .execute(&mut conn)
        .map_err(|e| {
            error!("Failed to update passkey counter: {e}");
            PasskeyError::DatabaseError
        })?;

        Ok(())
    }

    pub async fn cleanup_expired_challenges(&self) {
        let mut challenges = self.challenges.write().await;
        let cutoff = Utc::now() - Duration::seconds(CHALLENGE_TIMEOUT_SECONDS);
        challenges.retain(|_, c| c.created_at > cutoff);
    }
}

#[derive(Debug, Deserialize)]
struct ClientData {
    #[serde(rename = "type")]
    r#type: String,
    challenge: String,
    origin: String,
}

#[derive(Debug, Clone)]
pub enum PasskeyError {
    DatabaseError,
    ChallengeGenerationFailed,
    ChallengeStorageError,
    ChallengeNotFound,
    ChallengeExpired,
    InvalidClientData,
    InvalidCeremonyType,
    InvalidOrigin,
    InvalidChallenge,
    InvalidAttestationObject,
    InvalidAuthenticatorData,
    InvalidCredentialId,
    InvalidSignature,
    InvalidPublicKey,
    MissingUserId,
    NoAttestedCredential,
    RpIdMismatch,
    UserNotPresent,
    CounterMismatch,
    SignatureVerificationFailed,
    UnsupportedAlgorithm,
    PasskeyNotFound,
}

impl std::fmt::Display for PasskeyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::DatabaseError => write!(f, "Database error"),
            Self::ChallengeGenerationFailed => write!(f, "Challenge generation failed"),
            Self::ChallengeStorageError => write!(f, "Challenge storage error"),
            Self::ChallengeNotFound => write!(f, "Challenge not found"),
            Self::ChallengeExpired => write!(f, "Challenge expired"),
            Self::InvalidClientData => write!(f, "Invalid client data"),
            Self::InvalidCeremonyType => write!(f, "Invalid ceremony type"),
            Self::InvalidOrigin => write!(f, "Invalid origin"),
            Self::InvalidChallenge => write!(f, "Invalid challenge"),
            Self::InvalidAttestationObject => write!(f, "Invalid attestation object"),
            Self::InvalidAuthenticatorData => write!(f, "Invalid authenticator data"),
            Self::InvalidCredentialId => write!(f, "Invalid credential ID"),
            Self::InvalidSignature => write!(f, "Invalid signature"),
            Self::InvalidPublicKey => write!(f, "Invalid public key"),
            Self::MissingUserId => write!(f, "Missing user ID"),
            Self::NoAttestedCredential => write!(f, "No attested credential"),
            Self::RpIdMismatch => write!(f, "RP ID mismatch"),
            Self::UserNotPresent => write!(f, "User not present"),
            Self::CounterMismatch => write!(f, "Counter mismatch - possible cloning"),
            Self::SignatureVerificationFailed => write!(f, "Signature verification failed"),
            Self::UnsupportedAlgorithm => write!(f, "Unsupported algorithm"),
            Self::PasskeyNotFound => write!(f, "Passkey not found"),
        }
    }
}

impl std::error::Error for PasskeyError {}

impl IntoResponse for PasskeyError {
    fn into_response(self) -> axum::response::Response {
        let status = match self {
            Self::PasskeyNotFound => StatusCode::NOT_FOUND,
            Self::ChallengeExpired | Self::ChallengeNotFound => StatusCode::GONE,
            Self::InvalidOrigin | Self::RpIdMismatch => StatusCode::FORBIDDEN,
            Self::CounterMismatch | Self::SignatureVerificationFailed => StatusCode::UNAUTHORIZED,
            _ => StatusCode::BAD_REQUEST,
        };
        (status, self.to_string()).into_response()
    }
}

pub fn create_passkey_tables_migration() -> &'static str {
    r#"
    CREATE TABLE IF NOT EXISTS passkeys (
        id TEXT PRIMARY KEY,
        user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
        credential_id BYTEA NOT NULL UNIQUE,
        public_key BYTEA NOT NULL,
        counter BIGINT NOT NULL DEFAULT 0,
        name TEXT NOT NULL,
        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        last_used_at TIMESTAMPTZ,
        aaguid BYTEA,
        transports TEXT
    );

    CREATE INDEX IF NOT EXISTS idx_passkeys_user_id ON passkeys(user_id);
    CREATE INDEX IF NOT EXISTS idx_passkeys_credential_id ON passkeys(credential_id);
    "#
}

pub fn passkey_routes(_state: Arc<AppState>) -> Router<Arc<AppState>> {
    Router::new()
        .route("/registration/options", post(registration_options_handler))
        .route("/registration/verify", post(registration_verify_handler))
        .route("/authentication/options", post(authentication_options_handler))
        .route("/authentication/verify", post(authentication_verify_handler))
        .route("/list/:user_id", get(list_passkeys_handler))
        .route("/:user_id/:passkey_id", delete(delete_passkey_handler))
        .route("/:user_id/:passkey_id/rename", post(rename_passkey_handler))
        // Password fallback routes
        .route("/fallback/authenticate", post(password_fallback_handler))
        .route("/fallback/check/:username", get(check_fallback_available_handler))
        .route("/fallback/config", get(get_fallback_config_handler))
}

    async fn password_fallback_handler(
        State(state): State<Arc<AppState>>,
        Json(request): Json<PasswordFallbackRequest>,
    ) -> impl IntoResponse {
        let service = match get_passkey_service(&state) {
            Ok(s) => s,
            Err(e) => return e.into_response(),
        };
        match service.authenticate_with_password_fallback(&request).await {
            Ok(response) => Json(response).into_response(),
            Err(e) => e.into_response(),
        }
    }

    async fn check_fallback_available_handler(
        State(state): State<Arc<AppState>>,
        Path(username): Path<String>,
    ) -> impl IntoResponse {
        let service = match get_passkey_service(&state) {
            Ok(s) => s,
            Err(e) => return e.into_response(),
        };

        #[derive(Serialize)]
        struct FallbackAvailableResponse {
            available: bool,
            has_passkeys: bool,
            reason: Option<String>,
        }

        match service.should_offer_password_fallback(&username) {
            Ok(available) => {
                let has_passkeys = service.user_has_passkeys(&username).unwrap_or(false);
                Json(FallbackAvailableResponse {
                    available,
                    has_passkeys,
                    reason: if !available {
                        Some("Password fallback is disabled".to_string())
                    } else {
                        None
                    },
                }).into_response()
            }
            Err(e) => e.into_response(),
        }
    }

    async fn get_fallback_config_handler(
        State(state): State<Arc<AppState>>,
    ) -> impl IntoResponse {
        let service = match get_passkey_service(&state) {
            Ok(s) => s,
            Err(e) => return e.into_response(),
        };
        let config = service.get_fallback_config();

        #[derive(Serialize)]
        struct PublicFallbackConfig {
            enabled: bool,
            prompt_passkey_setup: bool,
        }

        Json(PublicFallbackConfig {
            enabled: config.enabled,
            prompt_passkey_setup: config.prompt_passkey_setup,
        }).into_response()
    }

async fn registration_options_handler(
    State(state): State<Arc<AppState>>,
    Json(request): Json<RegistrationOptionsRequest>,
) -> Result<Json<RegistrationOptions>, PasskeyError> {
    let service = get_passkey_service(&state)?;
    let options = service.generate_registration_options(request).await?;
    Ok(Json(options))
}

async fn registration_verify_handler(
    State(state): State<Arc<AppState>>,
    Json(request): Json<RegistrationVerifyRequest>,
) -> Result<Json<RegistrationResult>, PasskeyError> {
    let service = get_passkey_service(&state)?;
    let result = service.verify_registration(request.response, request.name).await?;
    Ok(Json(result))
}

async fn authentication_options_handler(
    State(state): State<Arc<AppState>>,
    Json(request): Json<AuthenticationOptionsRequest>,
) -> Result<Json<AuthenticationOptions>, PasskeyError> {
    let service = get_passkey_service(&state)?;
    let options = service.generate_authentication_options(request).await?;
    Ok(Json(options))
}

async fn authentication_verify_handler(
    State(state): State<Arc<AppState>>,
    Json(response): Json<AuthenticationResponse>,
) -> Result<Json<VerificationResult>, PasskeyError> {
    let service = get_passkey_service(&state)?;
    let result = service.verify_authentication(response).await?;
    Ok(Json(result))
}

async fn list_passkeys_handler(
    State(state): State<Arc<AppState>>,
    Path(user_id): Path<Uuid>,
) -> Result<Json<Vec<PasskeyInfo>>, PasskeyError> {
    let service = get_passkey_service(&state)?;
    let passkeys = service.list_passkeys(user_id)?;
    Ok(Json(passkeys))
}

async fn delete_passkey_handler(
    State(state): State<Arc<AppState>>,
    Path((user_id, passkey_id)): Path<(Uuid, String)>,
) -> Result<StatusCode, PasskeyError> {
    let service = get_passkey_service(&state)?;
    service.delete_passkey(user_id, &passkey_id)?;
    Ok(StatusCode::NO_CONTENT)
}

async fn rename_passkey_handler(
    State(state): State<Arc<AppState>>,
    Path((user_id, passkey_id)): Path<(Uuid, String)>,
    Json(request): Json<RenamePasskeyRequest>,
) -> Result<StatusCode, PasskeyError> {
    let service = get_passkey_service(&state)?;
    service.rename_passkey(user_id, &passkey_id, &request.name)?;
    Ok(StatusCode::OK)
}

#[derive(Debug, Deserialize)]
struct RegistrationVerifyRequest {
    response: RegistrationResponse,
    name: Option<String>,
}

fn get_passkey_service(state: &AppState) -> Result<PasskeyService, PasskeyError> {
    let pool = state.conn.clone();
    let rp_id = std::env::var("PASSKEY_RP_ID").unwrap_or_else(|_| "localhost".to_string());
    let rp_name = std::env::var("PASSKEY_RP_NAME").unwrap_or_else(|_| "General Bots".to_string());
    let rp_origin = std::env::var("PASSKEY_RP_ORIGIN").unwrap_or_else(|_| "http://localhost:8081".to_string());

    Ok(PasskeyService::new(pool, rp_id, rp_name, rp_origin))
}

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

    #[test]
    fn test_fallback_config_default() {
        let config = FallbackConfig::default();
        assert!(config.enabled);
        assert!(!config.require_additional_verification);
        assert_eq!(config.max_fallback_attempts, 5);
        assert_eq!(config.lockout_duration_seconds, 900);
        assert!(config.prompt_passkey_setup);
    }

    #[test]
    fn test_password_fallback_request_serialization() {
        let request = PasswordFallbackRequest {
            username: "testuser".to_string(),
            password: "testpass".to_string(),
        };
        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("testuser"));
    }

    #[test]
    fn test_password_fallback_response_structure() {
        let response = PasswordFallbackResponse {
            success: true,
            user_id: Some(Uuid::new_v4()),
            token: Some("test-token".to_string()),
            error: None,
            passkey_available: true,
        };
        assert!(response.success);
        assert!(response.user_id.is_some());
        assert!(response.passkey_available);
    }

    #[test]
    fn test_verification_result_with_fallback() {
        let result = VerificationResult {
            success: true,
            user_id: Some(Uuid::new_v4()),
            credential_id: None,
            error: None,
            used_fallback: true,
        };
        assert!(result.used_fallback);
    }



    #[test]
    fn test_passkey_error_display() {
        assert_eq!(PasskeyError::DatabaseError.to_string(), "Database error");
        assert_eq!(PasskeyError::ChallengeExpired.to_string(), "Challenge expired");
        assert_eq!(PasskeyError::PasskeyNotFound.to_string(), "Passkey not found");
    }

    #[test]
    fn test_challenge_operation_serialization() {
        let op = ChallengeOperation::Registration;
        let json = serde_json::to_string(&op).unwrap_or_default();
        assert!(json.contains("registration"));
    }

    #[test]
    fn test_registration_options_structure() {
        let options = RegistrationOptions {
            challenge: "test_challenge".to_string(),
            rp: RelyingParty {
                id: "example.com".to_string(),
                name: "Example".to_string(),
            },
            user: UserEntity {
                id: "user_id".to_string(),
                name: "user@example.com".to_string(),
                display_name: "User".to_string(),
            },
            pub_key_cred_params: vec![
                PubKeyCredParam {
                    cred_type: "public-key".to_string(),
                    alg: -7,
                },
            ],
            timeout: 60000,
            attestation: "none".to_string(),
            authenticator_selection: AuthenticatorSelection {
                authenticator_attachment: None,
                resident_key: "preferred".to_string(),
                require_resident_key: false,
                user_verification: "preferred".to_string(),
            },
            exclude_credentials: vec![],
        };

        assert_eq!(options.rp.id, "example.com");
        assert_eq!(options.timeout, 60000);
    }

    #[test]
    fn test_passkey_info_creation() {
        let info = PasskeyInfo {
            id: "pk_123".to_string(),
            name: "My Passkey".to_string(),
            created_at: Utc::now(),
            last_used_at: None,
        };

        assert_eq!(info.id, "pk_123");
        assert_eq!(info.name, "My Passkey");
        assert!(info.last_used_at.is_none());
    }
}
