use crate::domain::beneficiary::Beneficiary;
use crate::domain::record_id::RecordId;
use crate::ports::repository::{BeneficiaryRepositoryPort, VaultRepositoryPort};
use crate::ports::id_generator::IdGeneratorPort;
use crate::error::{Result, JottamError};

pub struct BeneficiaryUseCase<B: BeneficiaryRepositoryPort, V: VaultRepositoryPort, G: IdGeneratorPort> {
    ben_repo: B,
    vault_repo: V,
    id_gen: G,
}

impl<B: BeneficiaryRepositoryPort, V: VaultRepositoryPort, G: IdGeneratorPort> BeneficiaryUseCase<B, V, G> {
    pub fn new(ben_repo: B, vault_repo: V, id_gen: G) -> Self {
        Self { ben_repo, vault_repo, id_gen }
    }

    pub async fn create_beneficiary(&self, mut beneficiary: Beneficiary) -> Result<Beneficiary> {
        if self.ben_repo.find_by_email_and_user_id(&beneficiary.email, &beneficiary.user_id).await?.is_some() {
            return Err(JottamError::Conflict("A beneficiary with this email already exists".to_string()));
        }

        beneficiary.id = self.id_gen.generate();
        self.ben_repo.save(beneficiary).await
    }

    pub async fn get_beneficiary(&self, id: &RecordId, owner_id: &str) -> Result<Option<Beneficiary>> {
        let ben = self.ben_repo.find_by_id(id).await?;
        if let Some(ref b) = ben {
            if b.user_id != owner_id {
                return Err(JottamError::Unauthorized("Unauthorized access".to_string()));
            }
        }
        Ok(ben)
    }

    pub async fn list_beneficiaries_by_user(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<(Vec<Beneficiary>, u64)> {
        self.ben_repo.find_by_user_id(user_id, page, limit).await
    }

    pub async fn update_beneficiary(&self, mut beneficiary: Beneficiary, owner_id: &str) -> Result<Beneficiary> {
        let existing = self.ben_repo.find_by_id(&beneficiary.id).await?
            .ok_or_else(|| JottamError::NotFound("Beneficiary not found".to_string()))?;
        
        if existing.user_id != owner_id {
            return Err(JottamError::Unauthorized("Unauthorized to update".to_string()));
        }

        // Preserve existing vault keys
        beneficiary.vault_keys = existing.vault_keys;

        self.ben_repo.update(beneficiary).await
    }

    pub async fn delete_beneficiary(&self, id: &RecordId, owner_id: &str) -> Result<bool> {
        let existing = self.ben_repo.find_by_id(id).await?
            .ok_or_else(|| JottamError::NotFound("Beneficiary not found".to_string()))?;

        if existing.user_id != owner_id {
            return Err(JottamError::Unauthorized("Unauthorized to delete".to_string()));
        }

        let owner_record_id = RecordId::new(owner_id.to_string());
        self.vault_repo.remove_beneficiary_from_all_vaults(id, &owner_record_id).await?;
        self.ben_repo.delete(id).await
    }

    pub async fn verify_beneficiary_identity(&self, first_name: &str, last_name: &str) -> Result<Beneficiary> {
        let results = self.ben_repo.find_by_name(first_name, last_name).await?;
        
        if results.is_empty() {
             return Err(JottamError::NotFound("Beneficiary not found".to_string()));
        }
        
        if results.len() > 1 {
            return Err(JottamError::Conflict("Ambiguous name matches found. Please contact the vault owner.".to_string()));
        }

        let beneficiary = results.into_iter().next().unwrap();

        // Check if setup is complete
        if beneficiary.encrypted_secret_key.is_none() {
             return Err(JottamError::Unauthorized("This beneficiary account is not fully setup with security keys. Please contact the vault owner.".to_string()));
        }

        // Check verification questions
        if let Some(verification) = &beneficiary.verification {
             if verification.secret_question.is_none() {
                 return Err(JottamError::Unauthorized("Security questions not configured.".to_string()));
             }
        } else {
             return Err(JottamError::Unauthorized("Verification method not configured.".to_string()));
        }
        
        Ok(beneficiary)
    }

    pub async fn sync_beneficiary_vault_keys(&self, sync_items: Vec<(RecordId, crate::domain::beneficiary::VaultKey)>) -> Result<()> {
        let mut grouped_keys: std::collections::HashMap<RecordId, Vec<crate::domain::beneficiary::VaultKey>> = std::collections::HashMap::new();
        for (ben_id, key) in sync_items {
            grouped_keys.entry(ben_id).or_default().push(key);
        }

        for (beneficiary_id, new_keys) in grouped_keys {
            let beneficiary = self.ben_repo.find_by_id(&beneficiary_id).await?
                .ok_or_else(|| JottamError::NotFound(format!("Beneficiary {} not found", beneficiary_id)))?;

            let mut vault_keys = beneficiary.vault_keys.unwrap_or_default();
            
            for new_key in new_keys {
                if let Some(pos) = vault_keys.iter().position(|k| k.vault_id == new_key.vault_id) {
                    vault_keys[pos] = new_key;
                } else {
                    vault_keys.push(new_key);
                }
            }

            self.ben_repo.update_vault_keys(&beneficiary_id, vault_keys).await?;
        }
        Ok(())
    }
}
