#![allow(unused_imports)]
use super::*;
use crate::adapters::MemoryDatabaseAdapter;
use crate::types::{
    Account, CreateAccount, CreateUser, CreateVerification, Session, UpdateAccount, UpdateUser,
    User, Verification,
};
use std::sync::atomic::{AtomicU32, Ordering};

type DB = MemoryDatabaseAdapter;

// ── Unified counting hook ──────────────────────────────────────────
//
// A single hook struct that counts calls for *all* entity types,
// replacing the previous per-entity `CountingHook`,
// `AccountCountingHook`, and `VerificationCountingHook` structs.

struct CountingHook {
    // user
    before_create_user_count: AtomicU32,
    after_create_user_count: AtomicU32,
    before_update_user_count: AtomicU32,
    after_update_user_count: AtomicU32,
    before_delete_user_count: AtomicU32,
    after_delete_user_count: AtomicU32,
    // account
    before_create_account_count: AtomicU32,
    after_create_account_count: AtomicU32,
    before_update_account_count: AtomicU32,
    after_update_account_count: AtomicU32,
    before_delete_account_count: AtomicU32,
    after_delete_account_count: AtomicU32,
    // session
    before_create_session_count: AtomicU32,
    after_create_session_count: AtomicU32,
    before_delete_session_count: AtomicU32,
    after_delete_session_count: AtomicU32,
    // verification
    before_create_verification_count: AtomicU32,
    after_create_verification_count: AtomicU32,
    before_delete_verification_count: AtomicU32,
    after_delete_verification_count: AtomicU32,
}

impl CountingHook {
    fn new() -> Self {
        Self {
            before_create_user_count: AtomicU32::new(0),
            after_create_user_count: AtomicU32::new(0),
            before_update_user_count: AtomicU32::new(0),
            after_update_user_count: AtomicU32::new(0),
            before_delete_user_count: AtomicU32::new(0),
            after_delete_user_count: AtomicU32::new(0),
            before_create_account_count: AtomicU32::new(0),
            after_create_account_count: AtomicU32::new(0),
            before_update_account_count: AtomicU32::new(0),
            after_update_account_count: AtomicU32::new(0),
            before_delete_account_count: AtomicU32::new(0),
            after_delete_account_count: AtomicU32::new(0),
            before_create_session_count: AtomicU32::new(0),
            after_create_session_count: AtomicU32::new(0),
            before_delete_session_count: AtomicU32::new(0),
            after_delete_session_count: AtomicU32::new(0),
            before_create_verification_count: AtomicU32::new(0),
            after_create_verification_count: AtomicU32::new(0),
            before_delete_verification_count: AtomicU32::new(0),
            after_delete_verification_count: AtomicU32::new(0),
        }
    }
}

#[async_trait]
impl DatabaseHooks<DB> for CountingHook {
    // user hooks
    async fn before_create_user(&self, _user: &mut CreateUser) -> AuthResult<()> {
        self.before_create_user_count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_create_user(&self, _user: &User) -> AuthResult<()> {
        self.after_create_user_count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn before_update_user(&self, _id: &str, _update: &mut UpdateUser) -> AuthResult<()> {
        self.before_update_user_count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_update_user(&self, _user: &User) -> AuthResult<()> {
        self.after_update_user_count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn before_delete_user(&self, _id: &str) -> AuthResult<()> {
        self.before_delete_user_count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_delete_user(&self, _id: &str) -> AuthResult<()> {
        self.after_delete_user_count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    // session hooks
    async fn before_create_session(&self, _session: &mut CreateSession) -> AuthResult<()> {
        self.before_create_session_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_create_session(&self, _session: &Session) -> AuthResult<()> {
        self.after_create_session_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn before_delete_session(&self, _token: &str) -> AuthResult<()> {
        self.before_delete_session_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_delete_session(&self, _token: &str) -> AuthResult<()> {
        self.after_delete_session_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    // account hooks
    async fn before_create_account(&self, _account: &mut CreateAccount) -> AuthResult<()> {
        self.before_create_account_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_create_account(&self, _account: &Account) -> AuthResult<()> {
        self.after_create_account_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn before_update_account(
        &self,
        _id: &str,
        _update: &mut UpdateAccount,
    ) -> AuthResult<()> {
        self.before_update_account_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_update_account(&self, _account: &Account) -> AuthResult<()> {
        self.after_update_account_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn before_delete_account(&self, _id: &str) -> AuthResult<()> {
        self.before_delete_account_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_delete_account(&self, _id: &str) -> AuthResult<()> {
        self.after_delete_account_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    // verification hooks
    async fn before_create_verification(
        &self,
        _verification: &mut CreateVerification,
    ) -> AuthResult<()> {
        self.before_create_verification_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_create_verification(&self, _verification: &Verification) -> AuthResult<()> {
        self.after_create_verification_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn before_delete_verification(&self, _id: &str) -> AuthResult<()> {
        self.before_delete_verification_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    async fn after_delete_verification(&self, _id: &str) -> AuthResult<()> {
        self.after_delete_verification_count
            .fetch_add(1, Ordering::SeqCst);
        Ok(())
    }
}

// ── Rejection hooks (for testing rejection logic) ───────────────

struct RejectHook;
#[async_trait]
impl DatabaseHooks<DB> for RejectHook {
    async fn before_create_user(&self, _user: &mut CreateUser) -> AuthResult<()> {
        Err(crate::error::AuthError::forbidden("Hook rejected"))
    }
}

struct RejectAccountHook;
#[async_trait]
impl DatabaseHooks<DB> for RejectAccountHook {
    async fn before_create_account(&self, _account: &mut CreateAccount) -> AuthResult<()> {
        Err(crate::error::AuthError::forbidden("Account hook rejected"))
    }
}

struct RejectVerificationHook;
#[async_trait]
impl DatabaseHooks<DB> for RejectVerificationHook {
    async fn before_create_verification(&self, _v: &mut CreateVerification) -> AuthResult<()> {
        Err(crate::error::AuthError::forbidden(
            "Verification hook rejected",
        ))
    }
}

// ── Test helpers ───────────────────────────────────────────────────

/// Helper to construct a `CreateAccount` with sensible defaults.
fn test_create_account(user_id: &str) -> CreateAccount {
    CreateAccount {
        user_id: user_id.to_string(),
        account_id: "provider_123".to_string(),
        provider_id: "google".to_string(),
        access_token: None,
        refresh_token: None,
        id_token: None,
        access_token_expires_at: None,
        refresh_token_expires_at: None,
        scope: None,
        password: None,
    }
}

#[tokio::test]
async fn test_hooks_called_on_create_user() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let create = CreateUser::new()
        .with_email("test@example.com")
        .with_name("Test");
    db.create_user(create).await.unwrap();

    assert_eq!(hook.before_create_user_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook.after_create_user_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_hooks_called_on_update_user() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let create: CreateUser = CreateUser::new()
        .with_email("test@example.com")
        .with_name("Test");
    let user = db.create_user(create).await.unwrap();

    let update: UpdateUser = UpdateUser {
        name: Some("Updated".to_string()),
        email: None,
        image: None,
        email_verified: None,
        username: None,
        display_username: None,
        role: None,
        banned: None,
        ban_reason: None,
        ban_expires: None,
        two_factor_enabled: None,
        metadata: None,
        last_sign_in_at: None,
    };
    db.update_user(&user.id, update).await.unwrap();

    assert_eq!(hook.before_update_user_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook.after_update_user_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_hooks_called_on_delete_user() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let create = CreateUser::new()
        .with_email("test@example.com")
        .with_name("Test");
    let user = db.create_user(create).await.unwrap();

    db.delete_user(&user.id).await.unwrap();

    assert_eq!(hook.before_delete_user_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook.after_delete_user_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_before_hook_can_reject() {
    let hook: Arc<dyn DatabaseHooks<DB>> = Arc::new(RejectHook);
    let db = HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook);

    let create = CreateUser::new()
        .with_email("test@example.com")
        .with_name("Test");
    let result = db.create_user(create).await;

    assert!(result.is_err());
    assert_eq!(result.unwrap_err().status_code(), 403);
}

#[tokio::test]
async fn test_multiple_hooks() {
    let hook1 = Arc::new(CountingHook::new());
    let hook2 = Arc::new(CountingHook::new());
    let hook1_trait: Arc<dyn DatabaseHooks<DB>> = hook1.clone();
    let hook2_trait: Arc<dyn DatabaseHooks<DB>> = hook2.clone();
    let db = HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new()))
        .with_hook(hook1_trait)
        .with_hook(hook2_trait);

    let create = CreateUser::new()
        .with_email("test@example.com")
        .with_name("Test");
    db.create_user(create).await.unwrap();

    assert_eq!(hook1.before_create_user_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook2.before_create_user_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook1.after_create_user_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook2.after_create_user_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_passthrough_operations() {
    let db = HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new()));

    let result = db.get_user_by_email("nonexistent@test.com").await.unwrap();
    assert!(result.is_none());
}

// ── Account hook tests ──────────────────────────────────────────────

#[tokio::test]
async fn test_account_hooks_create() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let user = db
        .create_user(CreateUser::new().with_email("test@example.com"))
        .await
        .unwrap();

    db.create_account(test_create_account(&user.id))
        .await
        .unwrap();

    assert_eq!(hook.before_create_account_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook.after_create_account_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_account_hooks_update() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let user = db
        .create_user(CreateUser::new().with_email("test@example.com"))
        .await
        .unwrap();

    let created = db
        .create_account(test_create_account(&user.id))
        .await
        .unwrap();

    let update = UpdateAccount {
        access_token: Some("new_tok".to_string()),
        ..Default::default()
    };
    db.update_account(&created.id, update).await.unwrap();

    assert_eq!(hook.before_update_account_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook.after_update_account_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_account_hooks_delete() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let user = db
        .create_user(CreateUser::new().with_email("test@example.com"))
        .await
        .unwrap();

    let created = db
        .create_account(test_create_account(&user.id))
        .await
        .unwrap();

    db.delete_account(&created.id).await.unwrap();

    assert_eq!(hook.before_delete_account_count.load(Ordering::SeqCst), 1);
    assert_eq!(hook.after_delete_account_count.load(Ordering::SeqCst), 1);
}

#[tokio::test]
async fn test_account_before_hook_can_reject() {
    let hook = Arc::new(RejectAccountHook);
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let user = db
        .create_user(CreateUser::new().with_email("test@example.com"))
        .await
        .unwrap();

    let result = db.create_account(test_create_account(&user.id)).await;

    assert!(result.is_err());
    assert_eq!(result.unwrap_err().status_code(), 403);
}

// ── Verification hook tests ─────────────────────────────────────────

#[tokio::test]
async fn test_verification_hooks_create() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let verification = CreateVerification {
        identifier: "email_verification:test@example.com".to_string(),
        value: "token_abc".to_string(),
        expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
    };
    db.create_verification(verification).await.unwrap();

    assert_eq!(
        hook.before_create_verification_count.load(Ordering::SeqCst),
        1
    );
    assert_eq!(
        hook.after_create_verification_count.load(Ordering::SeqCst),
        1
    );
}

#[tokio::test]
async fn test_verification_hooks_delete() {
    let hook = Arc::new(CountingHook::new());
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let verification = CreateVerification {
        identifier: "email_verification:test@example.com".to_string(),
        value: "token_abc".to_string(),
        expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
    };
    let created = db.create_verification(verification).await.unwrap();

    db.delete_verification(&created.id).await.unwrap();

    assert_eq!(
        hook.before_delete_verification_count.load(Ordering::SeqCst),
        1
    );
    assert_eq!(
        hook.after_delete_verification_count.load(Ordering::SeqCst),
        1
    );
}

#[tokio::test]
async fn test_verification_before_hook_can_reject() {
    let hook = Arc::new(RejectVerificationHook);
    let hook_trait: Arc<dyn DatabaseHooks<DB>> = hook.clone();
    let db =
        HookedDatabaseAdapter::new(Arc::new(MemoryDatabaseAdapter::new())).with_hook(hook_trait);

    let verification = CreateVerification {
        identifier: "test".to_string(),
        value: "val".to_string(),
        expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
    };
    let result = db.create_verification(verification).await;

    assert!(result.is_err());
    assert_eq!(result.unwrap_err().status_code(), 403);
}
