use crate::AppService;
use crate::error::AppError;
use argon2::password_hash::{Salt, SaltString};
use argon2::{Argon2, PasswordHasher};
use models::users::{user, user_activity_log, user_email, user_password};
use models::workspaces::{WorkspaceRole, workspace, workspace_membership};
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use uuid::Uuid;

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct RegisterParams {
    pub username: String,
    pub email: String,
    pub password: String,
    pub captcha: String,
}

impl AppService {
    pub async fn auth_register(
        &self,
        params: RegisterParams,
        context: &Session,
    ) -> Result<user::Model, AppError> {
        self.auth_check_captcha(&context, params.captcha).await?;
        let password = self.auth_rsa_decode(&context, params.password).await?;
        if self
            .utils_find_user_by_username(params.username.clone())
            .await
            .is_ok()
        {
            slog::warn!(self.logs, "Registration failed: username already exists"; "username" => &params.username);
            return Err(AppError::UserNameExists);
        }

        if self
            .utils_find_user_by_email(params.email.clone())
            .await
            .is_ok()
        {
            slog::warn!(self.logs, "Registration failed: email already exists"; "email" => &params.email);
            return Err(AppError::EmailExists);
        }

        let user_uid = Uuid::now_v7();
        let now = chrono::Utc::now();
        let txn = self.db.begin().await.map_err(|_| AppError::TxnError)?;

        let user_model = user::ActiveModel {
            uid: Set(user_uid),
            username: Set(params.username.clone()),
            display_name: Set(Some(params.username.clone())),
            avatar_url: Set(None),
            website_url: Set(None),
            organization: Set(None),
            last_sign_in_at: Set(None),
            created_at: Set(now),
            updated_at: Set(now),
        };

        let user = user_model.insert(&txn).await.map_err(|e| {
            slog::error!(self.logs, "{}", format!("Failed to insert user: {:?}", e));
            AppError::UserNotFound
        })?;

        let user_email_model = user_email::ActiveModel {
            user: Set(user_uid),
            email: Set(params.email),
            created_at: Set(now),
        };

        user_email_model.insert(&txn).await.map_err(|e| {
            slog::error!(
                self.logs,
                "{}",
                format!("Failed to insert user email: {:?}", e)
            );
            AppError::UserNotFound
        })?;

        let salt = SaltString::generate(&mut rsa::rand_core::OsRng::default());
        let password_hash = Argon2::default()
            .hash_password(password.as_bytes(), Salt::from_b64(&*salt.to_string())?)
            .map_err(|e| {
                slog::error!(self.logs, "{}", format!("Failed to hash password: {:?}", e));
                AppError::UserNotFound
            })?
            .to_string();
        let user_password_model = user_password::ActiveModel {
            user: Set(user_uid),
            password_hash: Set(password_hash),
            password_salt: Set(Some(salt.to_string())),
            is_active: Set(true),
            created_at: Set(now),
            updated_at: Set(now),
        };

        user_password_model.insert(&txn).await.map_err(|e| {
            slog::error!(
                self.logs,
                "{}",
                format!("Failed to insert user password: {:?}", e)
            );
            AppError::UserNotFound
        })?;

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Option::from(user_uid)),
            action: Set("register".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "username": user.username.clone(),
                "method": "password"
            })
            .into()),
            created_at: Set(now),
            ..Default::default()
        }
        .insert(&txn)
        .await;

        // Auto-create personal workspace for the new user
        let personal_slug = format!("~{}", params.username);
        let ws = workspace::ActiveModel {
            id: Set(Uuid::now_v7()),
            slug: Set(personal_slug),
            name: Set(format!("{} 的工作空间", params.username)),
            description: Set(None),
            avatar_url: Set(None),
            plan: Set("free".to_string()),
            billing_email: Set(None),
            stripe_customer_id: Set(None),
            stripe_subscription_id: Set(None),
            plan_expires_at: Set(None),
            deleted_at: Set(None),
            created_at: Set(now),
            updated_at: Set(now),
        };
        let ws = ws.insert(&txn).await.map_err(|e| {
            slog::error!(
                self.logs,
                "{}",
                format!("Failed to insert personal workspace: {:?}", e)
            );
            AppError::UserNotFound
        })?;

        let _ = workspace_membership::ActiveModel {
            id: Default::default(),
            workspace_id: Set(ws.id),
            user_id: Set(user_uid),
            role: Set(WorkspaceRole::Owner.to_string()),
            status: Set("active".to_string()),
            invited_by: Set(None),
            joined_at: Set(now),
            invite_token: Set(None),
            invite_expires_at: Set(None),
        }
        .insert(&txn)
        .await;
        txn.commit().await.map_err(|_| AppError::TxnError)?;
        context.set_user(user_uid);
        context.set_current_workspace_id(ws.id);
        context.remove(Self::RSA_PRIVATE_KEY);
        context.remove(Self::RSA_PUBLIC_KEY);
        slog::info!(self.logs, "User registered successfully"; "user_uid" => %user_uid, "username" => &user.username);
        Ok(user)
    }
}
