use crate::domain::jot::Jot;
use crate::domain::vault::Vault;
use crate::domain::beneficiary::{Beneficiary, VaultKey};
use crate::domain::user::User;
use crate::domain::access_link::AccessLink;
pub use crate::domain::guest_share::GuestShare;
pub use crate::domain::pairing_session::PairingSession;
use crate::domain::record_id::RecordId;
use crate::error::Result;
use async_trait::async_trait;

#[async_trait]
pub trait JotRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<Jot>>;
    async fn find_by_user_id(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<Vec<Jot>>;
    async fn count_by_user_id(&self, user_id: &RecordId) -> Result<u64>;
    async fn find_by_vault_id_or_user_id(&self, vault_id: &RecordId, user_id: &RecordId, page: u32, limit: u32) -> Result<Vec<Jot>>;
    async fn count_by_vault_id(&self, vault_id: &RecordId) -> Result<u64>;
    async fn save(&self, jot: Jot) -> Result<Jot>;
    async fn update(&self, jot: Jot) -> Result<Jot>;
    async fn delete(&self, id: &RecordId) -> Result<bool>;
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64>;
    async fn update_vault_id_for_jots(&self, jot_ids: &[RecordId], owner_id: &RecordId, vault_id: Option<RecordId>) -> Result<()>;
    async fn clear_vault_id_for_all_jots_in_vault(&self, vault_id: &RecordId, owner_id: &RecordId) -> Result<()>;
    async fn find_by_ids(&self, ids: &[RecordId]) -> Result<Vec<Jot>>;
}

#[async_trait]
impl<T: JotRepositoryPort + ?Sized + Send + Sync> JotRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<Jot>> { self.as_ref().find_by_id(id).await }
    async fn find_by_user_id(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<Vec<Jot>> { self.as_ref().find_by_user_id(user_id, page, limit).await }
    async fn count_by_user_id(&self, user_id: &RecordId) -> Result<u64> { self.as_ref().count_by_user_id(user_id).await }
    async fn find_by_vault_id_or_user_id(&self, vault_id: &RecordId, user_id: &RecordId, page: u32, limit: u32) -> Result<Vec<Jot>> { self.as_ref().find_by_vault_id_or_user_id(vault_id, user_id, page, limit).await }
    async fn count_by_vault_id(&self, vault_id: &RecordId) -> Result<u64> { self.as_ref().count_by_vault_id(vault_id).await }
    async fn save(&self, jot: Jot) -> Result<Jot> { self.as_ref().save(jot).await }
    async fn update(&self, jot: Jot) -> Result<Jot> { self.as_ref().update(jot).await }
    async fn delete(&self, id: &RecordId) -> Result<bool> { self.as_ref().delete(id).await }
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64> { self.as_ref().delete_by_user_id(user_id).await }
    async fn update_vault_id_for_jots(&self, jot_ids: &[RecordId], owner_id: &RecordId, vault_id: Option<RecordId>) -> Result<()> { self.as_ref().update_vault_id_for_jots(jot_ids, owner_id, vault_id).await }
    async fn clear_vault_id_for_all_jots_in_vault(&self, vault_id: &RecordId, owner_id: &RecordId) -> Result<()> { self.as_ref().clear_vault_id_for_all_jots_in_vault(vault_id, owner_id).await }
    async fn find_by_ids(&self, ids: &[RecordId]) -> Result<Vec<Jot>> { self.as_ref().find_by_ids(ids).await }
}

#[async_trait]
pub trait VaultRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<Vault>>;
    async fn find_by_user_id(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<Vec<Vault>>;
    async fn count_by_user_id(&self, user_id: &RecordId) -> Result<u64>;
    async fn find_by_name_and_user_id(&self, name: &str, user_id: &RecordId) -> Result<Option<Vault>>;
    async fn save(&self, vault: Vault) -> Result<Vault>;
    async fn update(&self, id: &RecordId, vault: Vault) -> Result<Vault>;
    async fn delete(&self, id: &RecordId) -> Result<bool>;
    async fn remove_beneficiary_from_all_vaults(&self, beneficiary_id: &RecordId, user_id: &RecordId) -> Result<()>;
    async fn remove_jot_from_all_vaults(&self, jot_id: &RecordId, user_id: &RecordId) -> Result<()>;
    async fn fetch_all(&self) -> Result<Vec<Vault>>;
    async fn find_due_vaults(&self, now: chrono::DateTime<chrono::Utc>) -> Result<Vec<Vault>>;
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64>;
}

#[async_trait]
impl<T: VaultRepositoryPort + ?Sized + Send + Sync> VaultRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<Vault>> { self.as_ref().find_by_id(id).await }
    async fn find_by_user_id(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<Vec<Vault>> { self.as_ref().find_by_user_id(user_id, page, limit).await }
    async fn count_by_user_id(&self, user_id: &RecordId) -> Result<u64> { self.as_ref().count_by_user_id(user_id).await }
    async fn find_by_name_and_user_id(&self, name: &str, user_id: &RecordId) -> Result<Option<Vault>> { self.as_ref().find_by_name_and_user_id(name, user_id).await }
    async fn save(&self, vault: Vault) -> Result<Vault> { self.as_ref().save(vault).await }
    async fn update(&self, id: &RecordId, vault: Vault) -> Result<Vault> { self.as_ref().update(id, vault).await }
    async fn delete(&self, id: &RecordId) -> Result<bool> { self.as_ref().delete(id).await }
    async fn remove_beneficiary_from_all_vaults(&self, beneficiary_id: &RecordId, user_id: &RecordId) -> Result<()> { self.as_ref().remove_beneficiary_from_all_vaults(beneficiary_id, user_id).await }
    async fn remove_jot_from_all_vaults(&self, jot_id: &RecordId, user_id: &RecordId) -> Result<()> { self.as_ref().remove_jot_from_all_vaults(jot_id, user_id).await }
    async fn fetch_all(&self) -> Result<Vec<Vault>> { self.as_ref().fetch_all().await }
    async fn find_due_vaults(&self, now: chrono::DateTime<chrono::Utc>) -> Result<Vec<Vault>> { self.as_ref().find_due_vaults(now).await }
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64> { self.as_ref().delete_by_user_id(user_id).await }
}

#[async_trait]
pub trait BeneficiaryRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<Beneficiary>>;
    async fn find_by_user_id(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<(Vec<Beneficiary>, u64)>;
    async fn save(&self, beneficiary: Beneficiary) -> Result<Beneficiary>;
    async fn update(&self, beneficiary: Beneficiary) -> Result<Beneficiary>;
    async fn delete(&self, id: &RecordId) -> Result<bool>;
    async fn find_by_ids(&self, ids: &[RecordId]) -> Result<Vec<Beneficiary>>;
    async fn find_by_ids_and_user_id(&self, ids: &[RecordId], user_id: &str) -> Result<Vec<Beneficiary>>;
    async fn update_vault_keys(&self, id: &RecordId, keys: Vec<VaultKey>) -> Result<()>;
    async fn find_by_name(&self, first_name: &str, last_name: &str) -> Result<Vec<Beneficiary>>;
    async fn find_by_email_and_user_id(&self, email: &str, user_id: &str) -> Result<Option<Beneficiary>>;
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64>;
}

#[async_trait]
impl<T: BeneficiaryRepositoryPort + ?Sized + Send + Sync> BeneficiaryRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<Beneficiary>> { self.as_ref().find_by_id(id).await }
    async fn find_by_user_id(&self, user_id: &RecordId, page: u32, limit: u32) -> Result<(Vec<Beneficiary>, u64)> { self.as_ref().find_by_user_id(user_id, page, limit).await }
    async fn save(&self, beneficiary: Beneficiary) -> Result<Beneficiary> { self.as_ref().save(beneficiary).await }
    async fn update(&self, beneficiary: Beneficiary) -> Result<Beneficiary> { self.as_ref().update(beneficiary).await }
    async fn delete(&self, id: &RecordId) -> Result<bool> { self.as_ref().delete(id).await }
    async fn find_by_ids(&self, ids: &[RecordId]) -> Result<Vec<Beneficiary>> { self.as_ref().find_by_ids(ids).await }
    async fn find_by_ids_and_user_id(&self, ids: &[RecordId], user_id: &str) -> Result<Vec<Beneficiary>> { self.as_ref().find_by_ids_and_user_id(ids, user_id).await }
    async fn update_vault_keys(&self, id: &RecordId, keys: Vec<VaultKey>) -> Result<()> { self.as_ref().update_vault_keys(id, keys).await }
    async fn find_by_name(&self, first_name: &str, last_name: &str) -> Result<Vec<Beneficiary>> { self.as_ref().find_by_name(first_name, last_name).await }
    async fn find_by_email_and_user_id(&self, email: &str, user_id: &str) -> Result<Option<Beneficiary>> { self.as_ref().find_by_email_and_user_id(email, user_id).await }
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64> { self.as_ref().delete_by_user_id(user_id).await }
}

#[async_trait]
pub trait UserRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<User>>;
    async fn find_by_email(&self, email: &str) -> Result<Option<User>>;
    async fn save(&self, user: User) -> Result<User>;
    async fn update(&self, user: User) -> Result<User>;
    async fn delete(&self, id: &RecordId) -> Result<bool>;
}

#[async_trait]
impl<T: UserRepositoryPort + ?Sized + Send + Sync> UserRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<User>> { self.as_ref().find_by_id(id).await }
    async fn find_by_email(&self, email: &str) -> Result<Option<User>> { self.as_ref().find_by_email(email).await }
    async fn save(&self, user: User) -> Result<User> { self.as_ref().save(user).await }
    async fn update(&self, user: User) -> Result<User> { self.as_ref().update(user).await }
    async fn delete(&self, id: &RecordId) -> Result<bool> { self.as_ref().delete(id).await }
}

#[async_trait]
pub trait AccessLinkRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<AccessLink>>;
    async fn save(&self, access_link: AccessLink) -> Result<AccessLink>;
    async fn update(&self, access_link: AccessLink) -> Result<AccessLink>;
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64>;
}

#[async_trait]
impl<T: AccessLinkRepositoryPort + ?Sized + Send + Sync> AccessLinkRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<AccessLink>> { self.as_ref().find_by_id(id).await }
    async fn save(&self, access_link: AccessLink) -> Result<AccessLink> { self.as_ref().save(access_link).await }
    async fn update(&self, access_link: AccessLink) -> Result<AccessLink> { self.as_ref().update(access_link).await }
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64> { self.as_ref().delete_by_user_id(user_id).await }
}

#[async_trait]
pub trait GuestShareRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<GuestShare>>;
    async fn save(&self, guest_share: GuestShare) -> Result<GuestShare>;
    async fn delete(&self, id: &RecordId) -> Result<bool>;
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64>;
}

#[async_trait]
impl<T: GuestShareRepositoryPort + ?Sized + Send + Sync> GuestShareRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<GuestShare>> { self.as_ref().find_by_id(id).await }
    async fn save(&self, guest_share: GuestShare) -> Result<GuestShare> { self.as_ref().save(guest_share).await }
    async fn delete(&self, id: &RecordId) -> Result<bool> { self.as_ref().delete(id).await }
    async fn delete_by_user_id(&self, user_id: &str) -> Result<u64> { self.as_ref().delete_by_user_id(user_id).await }
}

#[async_trait]
pub trait PairingSessionRepositoryPort: Send + Sync {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<PairingSession>>;
    async fn save(&self, session: PairingSession) -> Result<PairingSession>;
    async fn update_share_id(&self, id: &RecordId, share_id: String) -> Result<()>;
    async fn delete(&self, id: &RecordId) -> Result<bool>;
}

#[async_trait]
impl<T: PairingSessionRepositoryPort + ?Sized + Send + Sync> PairingSessionRepositoryPort for std::sync::Arc<T> {
    async fn find_by_id(&self, id: &RecordId) -> Result<Option<PairingSession>> { self.as_ref().find_by_id(id).await }
    async fn save(&self, session: PairingSession) -> Result<PairingSession> { self.as_ref().save(session).await }
    async fn update_share_id(&self, id: &RecordId, share_id: String) -> Result<()> { self.as_ref().update_share_id(id, share_id).await }
    async fn delete(&self, id: &RecordId) -> Result<bool> { self.as_ref().delete(id).await }
}
