use crate::domain::user::{User, MasterKeyData, BeneficiaryKeyData, UserVaultKeyData};
use crate::domain::record_id::RecordId;
use crate::ports::repository::UserRepositoryPort;
use crate::ports::email::EmailPort;
use crate::error::Result;
use chrono::Utc;

pub struct UserUseCase<U: UserRepositoryPort, E: EmailPort> {
    user_repo: U,
    email_port: E,
}

impl<U: UserRepositoryPort, E: EmailPort> UserUseCase<U, E> {
    pub fn new(user_repo: U, email_port: E) -> Self {
        Self { user_repo, email_port }
    }

    pub async fn get_user_by_email(&self, email: &str) -> Result<Option<User>> {
        self.user_repo.find_by_email(email).await
    }

    pub async fn get_user_by_id(&self, id: &RecordId) -> Result<Option<User>> {
        self.user_repo.find_by_id(id).await
    }

    pub async fn create_user(&self, user: User) -> Result<User> {
        let first_name = user.first_name.clone();
        let email = user.email.clone();
        let saved_user = self.user_repo.save(user).await?;
        
        // Send welcome email
        let _ = self.email_port.send_welcome_email(&email, &first_name).await;
        
        Ok(saved_user)
    }

    pub async fn update_user(&self, user: User) -> Result<User> {
        self.user_repo.update(user).await
    }

    pub async fn sync_account_keys(&self, user_email: &str, key_data: MasterKeyData) -> Result<()> {
        let mut user = self.user_repo.find_by_email(user_email).await?
            .ok_or_else(|| crate::error::JottamError::NotFound("User not found".to_string()))?;

        if user.master_key_data.is_some() {
            return Ok(());
        }

        user.master_key_data = Some(key_data);
        user.sync_count += 1;
        user.last_synced_at = Some(Utc::now());
        user.updated_at = Utc::now();
        
        self.user_repo.update(user).await?;
        Ok(())
    }

    pub async fn sync_beneficiary_keys(&self, user_email: &str, beneficiary_keys: Vec<BeneficiaryKeyData>) -> Result<()> {
        let mut user = self.user_repo.find_by_email(user_email).await?
            .ok_or_else(|| crate::error::JottamError::NotFound("User not found".to_string()))?;

        let mut existing_keys = user.beneficiary_keys.unwrap_or_default();

        for new_key in beneficiary_keys {
            if let Some(pos) = existing_keys.iter().position(|k| k.beneficiary_id == new_key.beneficiary_id) {
                existing_keys[pos] = new_key;
            } else {
                existing_keys.push(new_key);
            }
        }

        user.beneficiary_keys = Some(existing_keys);
        user.sync_count += 1;
        user.last_synced_at = Some(Utc::now());
        user.updated_at = Utc::now();
        
        self.user_repo.update(user).await?;
        Ok(())
    }

    pub async fn sync_user_vault_keys(&self, user_email: &str, vault_key_data: UserVaultKeyData) -> Result<()> {
        let mut user = self.user_repo.find_by_email(user_email).await?
            .ok_or_else(|| crate::error::JottamError::NotFound("User not found".to_string()))?;

        let mut existing_keys = user.vault_keys.unwrap_or_default();

        if let Some(pos) = existing_keys.iter().position(|k| k.vault_id == vault_key_data.vault_id) {
            existing_keys[pos] = vault_key_data;
        } else {
            existing_keys.push(vault_key_data);
        }

        user.vault_keys = Some(existing_keys);
        user.sync_count += 1;
        user.last_synced_at = Some(Utc::now());
        user.updated_at = Utc::now();
        
        self.user_repo.update(user).await?;
        Ok(())
    }
}
