use barycenter::entities;
use barycenter::storage;
use sea_orm::DatabaseConnection;

/// Builder for creating test users
pub struct UserBuilder {
    username: String,
    password: String,
    email: Option<String>,
    enabled: bool,
    requires_2fa: bool,
}

impl UserBuilder {
    pub fn new(username: &str) -> Self {
        Self {
            username: username.to_string(),
            password: "password123".to_string(),
            email: None,
            enabled: true,
            requires_2fa: false,
        }
    }

    pub fn with_password(mut self, password: &str) -> Self {
        self.password = password.to_string();
        self
    }

    pub fn with_email(mut self, email: &str) -> Self {
        self.email = Some(email.to_string());
        self
    }

    pub fn requires_2fa(mut self) -> Self {
        self.requires_2fa = true;
        self
    }

    pub fn disabled(mut self) -> Self {
        self.enabled = false;
        self
    }

    pub async fn create(self, db: &DatabaseConnection) -> storage::User {
        let user = storage::create_user(db, &self.username, &self.password, self.email)
            .await
            .expect("Failed to create test user");

        // Update 2FA and enabled flags if needed
        if !self.enabled || self.requires_2fa {
            storage::update_user(
                db,
                &user.subject,
                self.enabled,
                None,
                if self.requires_2fa { Some(true) } else { None },
            )
            .await
            .expect("Failed to update user flags");

            // Retrieve updated user
            storage::get_user_by_subject(db, &user.subject)
                .await
                .expect("Failed to get updated user")
                .expect("User not found")
        } else {
            user
        }
    }
}

/// Builder for creating test OAuth clients
pub struct ClientBuilder {
    client_name: Option<String>,
    redirect_uris: Vec<String>,
}

impl ClientBuilder {
    pub fn new() -> Self {
        Self {
            client_name: Some("Test Client".to_string()),
            redirect_uris: vec!["http://localhost:3000/callback".to_string()],
        }
    }

    pub fn with_name(mut self, name: &str) -> Self {
        self.client_name = Some(name.to_string());
        self
    }

    pub fn with_redirect_uri(mut self, uri: &str) -> Self {
        self.redirect_uris = vec![uri.to_string()];
        self
    }

    pub fn with_redirect_uris(mut self, uris: Vec<String>) -> Self {
        self.redirect_uris = uris;
        self
    }

    pub async fn create(self, db: &DatabaseConnection) -> storage::Client {
        storage::create_client(
            db,
            storage::NewClient {
                client_name: self.client_name,
                redirect_uris: self.redirect_uris,
            },
        )
        .await
        .expect("Failed to create test client")
    }
}

impl Default for ClientBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// Builder for creating test sessions
pub struct SessionBuilder {
    subject: String,
    auth_time: i64,
    ttl: i64,
    amr: Option<Vec<String>>,
    acr: Option<String>,
    mfa_verified: bool,
}

impl SessionBuilder {
    pub fn new(subject: &str) -> Self {
        Self {
            subject: subject.to_string(),
            auth_time: chrono::Utc::now().timestamp(),
            ttl: 3600, // 1 hour
            amr: None,
            acr: None,
            mfa_verified: false,
        }
    }

    pub fn with_auth_time(mut self, auth_time: i64) -> Self {
        self.auth_time = auth_time;
        self
    }

    pub fn with_ttl(mut self, ttl: i64) -> Self {
        self.ttl = ttl;
        self
    }

    pub fn with_amr(mut self, amr: Vec<String>) -> Self {
        self.amr = Some(amr);
        self
    }

    pub fn with_acr(mut self, acr: &str) -> Self {
        self.acr = Some(acr.to_string());
        self
    }

    pub fn with_mfa_verified(mut self) -> Self {
        self.mfa_verified = true;
        self
    }

    pub async fn create(self, db: &DatabaseConnection) -> storage::Session {
        let session =
            storage::create_session(db, &self.subject, self.auth_time, self.ttl, None, None)
                .await
                .expect("Failed to create test session");

        // Update AMR/ACR/MFA if needed
        if self.amr.is_some() || self.acr.is_some() || self.mfa_verified {
            let amr_json = self.amr.map(|a| serde_json::to_string(&a).unwrap());
            storage::update_session_auth_context(
                db,
                &session.session_id,
                amr_json.as_deref(),
                self.acr.as_deref(),
                if self.mfa_verified { Some(true) } else { None },
            )
            .await
            .expect("Failed to update session auth context");

            // Retrieve updated session
            storage::get_session(db, &session.session_id)
                .await
                .expect("Failed to get updated session")
                .expect("Session not found")
        } else {
            session
        }
    }
}

/// Builder for creating test passkeys
pub struct PasskeyBuilder {
    subject: String,
    name: Option<String>,
    backup_state: bool,
    backup_eligible: bool,
}

impl PasskeyBuilder {
    pub fn new(subject: &str) -> Self {
        Self {
            subject: subject.to_string(),
            name: None,
            backup_state: false,
            backup_eligible: false,
        }
    }

    pub fn with_name(mut self, name: &str) -> Self {
        self.name = Some(name.to_string());
        self
    }

    pub fn cloud_synced(mut self) -> Self {
        self.backup_state = true;
        self.backup_eligible = true;
        self
    }

    pub fn hardware_bound(mut self) -> Self {
        self.backup_state = false;
        self.backup_eligible = false;
        self
    }

    pub async fn create(self, db: &DatabaseConnection) -> entities::passkey::Model {
        // Create a minimal test passkey
        // In real tests, you'd use MockWebAuthnCredential to generate this
        use webauthn_rs::prelude::*;

        // Create a test passkey with minimal data
        use base64ct::{Base64UrlUnpadded, Encoding};
        let credential_id = uuid::Uuid::new_v4().as_bytes().to_vec();
        let cred_id_b64 = Base64UrlUnpadded::encode_string(&credential_id);

        let passkey_json = serde_json::json!({
            "cred_id": &cred_id_b64,
            "cred": {
                "counter": 0,
                "backup_state": self.backup_state,
                "backup_eligible": self.backup_eligible
            }
        });

        storage::create_passkey(
            db,
            &cred_id_b64,
            &self.subject,
            &serde_json::to_string(&passkey_json).unwrap(),
            0,
            None,
            self.backup_eligible,
            self.backup_state,
            None,
            self.name.as_deref(),
        )
        .await
        .expect("Failed to create test passkey")
    }
}
