use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::Arc;

use crate::adapters::DatabaseAdapter;
use crate::adapters::database::{
    AccountOps, ApiKeyOps, InvitationOps, MemberOps, OrganizationOps, SessionOps, TwoFactorOps,
    UserOps, VerificationOps,
};
use crate::error::AuthResult;
use crate::types::{
    AdminAuditLog, AthenaClient, CreateAccount, CreateAdminAuditLog, CreateApiKey,
    CreateAthenaClient, CreateEmailSendFailure, CreateEmailTemplate, CreateInvitation,
    CreateMember, CreateOAuthClient, CreateOrganization, CreateSentEmail, CreateSession,
    CreateTwoFactor, CreateUser, CreateVerification, EmailEventType, EmailSendFailure,
    EmailTemplate, InvitationStatus, ListAdminAuditLogsParams, ListEmailEventTypesParams,
    ListEmailSendFailuresParams, ListEmailTemplatesParams, ListSentEmailsParams, ListUsersParams,
    OAuthClient, SentEmail, UpdateAccount, UpdateApiKey, UpdateEmailSendFailure,
    UpdateEmailTemplate, UpdateOrganization, UpdateSentEmail, UpdateUser,
};

/// Database lifecycle hooks for intercepting operations.
///
/// All methods have default no-op implementations. Override only the hooks
/// you need. Returning `Err` from a `before_*` hook aborts the operation.
///
/// The `DB` type parameter determines the concrete entity types used in
/// `after_*` hooks (e.g., `after_create_user` receives `&DB::User`).
#[async_trait]
pub trait DatabaseHooks<DB: DatabaseAdapter>: Send + Sync {
    async fn before_create_user(&self, user: &mut CreateUser) -> AuthResult<()> {
        let _ = user;
        Ok(())
    }

    async fn after_create_user(&self, user: &DB::User) -> AuthResult<()> {
        let _ = user;
        Ok(())
    }

    async fn before_update_user(&self, id: &str, update: &mut UpdateUser) -> AuthResult<()> {
        let _ = (id, update);
        Ok(())
    }

    async fn after_update_user(&self, user: &DB::User) -> AuthResult<()> {
        let _ = user;
        Ok(())
    }

    async fn before_delete_user(&self, id: &str) -> AuthResult<()> {
        let _ = id;
        Ok(())
    }

    async fn after_delete_user(&self, id: &str) -> AuthResult<()> {
        let _ = id;
        Ok(())
    }

    async fn before_create_session(&self, session: &mut CreateSession) -> AuthResult<()> {
        let _ = session;
        Ok(())
    }

    async fn after_create_session(&self, session: &DB::Session) -> AuthResult<()> {
        let _ = session;
        Ok(())
    }

    async fn before_delete_session(&self, token: &str) -> AuthResult<()> {
        let _ = token;
        Ok(())
    }

    async fn after_delete_session(&self, token: &str) -> AuthResult<()> {
        let _ = token;
        Ok(())
    }

    // ── Account hooks ──────────────────────────────────────────────────

    async fn before_create_account(&self, account: &mut CreateAccount) -> AuthResult<()> {
        let _ = account;
        Ok(())
    }

    async fn after_create_account(&self, account: &DB::Account) -> AuthResult<()> {
        let _ = account;
        Ok(())
    }

    async fn before_update_account(&self, id: &str, update: &mut UpdateAccount) -> AuthResult<()> {
        let _ = (id, update);
        Ok(())
    }

    async fn after_update_account(&self, account: &DB::Account) -> AuthResult<()> {
        let _ = account;
        Ok(())
    }

    async fn before_delete_account(&self, id: &str) -> AuthResult<()> {
        let _ = id;
        Ok(())
    }

    async fn after_delete_account(&self, id: &str) -> AuthResult<()> {
        let _ = id;
        Ok(())
    }

    // ── Verification hooks ─────────────────────────────────────────────

    async fn before_create_verification(
        &self,
        verification: &mut CreateVerification,
    ) -> AuthResult<()> {
        let _ = verification;
        Ok(())
    }

    async fn after_create_verification(&self, verification: &DB::Verification) -> AuthResult<()> {
        let _ = verification;
        Ok(())
    }

    async fn before_delete_verification(&self, id: &str) -> AuthResult<()> {
        let _ = id;
        Ok(())
    }

    async fn after_delete_verification(&self, id: &str) -> AuthResult<()> {
        let _ = id;
        Ok(())
    }
}

/// A database adapter wrapper that calls hooks around the inner adapter's operations.
pub struct HookedDatabaseAdapter<DB: DatabaseAdapter> {
    inner: Arc<DB>,
    hooks: Vec<Arc<dyn DatabaseHooks<DB>>>,
}

impl<DB: DatabaseAdapter> HookedDatabaseAdapter<DB> {
    pub fn new(inner: Arc<DB>) -> Self {
        Self {
            inner,
            hooks: Vec::new(),
        }
    }

    pub fn with_hook(mut self, hook: Arc<dyn DatabaseHooks<DB>>) -> Self {
        self.hooks.push(hook);
        self
    }

    pub fn add_hook(&mut self, hook: Arc<dyn DatabaseHooks<DB>>) {
        self.hooks.push(hook);
    }
}

// ---------------------------------------------------------------------------
// Helper macros to eliminate repeated before→execute→after hook patterns.
// ---------------------------------------------------------------------------

/// Hook pattern for **create** / **update** operations that take a mutable
/// input, call the inner adapter, and return the resulting entity.
macro_rules! hooked_create {
    ($self:ident, $before:ident, $after:ident, $inner_method:ident, $input:ident) => {{
        for hook in &$self.hooks {
            hook.$before(&mut $input).await?;
        }
        let result = $self.inner.$inner_method($input).await?;
        for hook in &$self.hooks {
            hook.$after(&result).await?;
        }
        Ok(result)
    }};
}

/// Hook pattern for **update** operations that take `(id, &mut input)`.
macro_rules! hooked_update {
    ($self:ident, $before:ident, $after:ident, $inner_method:ident, $id:expr, $input:ident) => {{
        for hook in &$self.hooks {
            hook.$before($id, &mut $input).await?;
        }
        let result = $self.inner.$inner_method($id, $input).await?;
        for hook in &$self.hooks {
            hook.$after(&result).await?;
        }
        Ok(result)
    }};
}

/// Hook pattern for **delete** operations that take a single `&str` key and
/// return `()`.
macro_rules! hooked_delete {
    ($self:ident, $before:ident, $after:ident, $inner_method:ident, $key:expr) => {{
        for hook in &$self.hooks {
            hook.$before($key).await?;
        }
        $self.inner.$inner_method($key).await?;
        for hook in &$self.hooks {
            hook.$after($key).await?;
        }
        Ok(())
    }};
}

#[async_trait]
impl<DB: DatabaseAdapter> UserOps for HookedDatabaseAdapter<DB> {
    type User = DB::User;

    async fn create_user(&self, mut user: CreateUser) -> AuthResult<Self::User> {
        hooked_create!(
            self,
            before_create_user,
            after_create_user,
            create_user,
            user
        )
    }

    async fn get_user_by_id(&self, id: &str) -> AuthResult<Option<Self::User>> {
        self.inner.get_user_by_id(id).await
    }

    async fn get_user_by_email(&self, email: &str) -> AuthResult<Option<Self::User>> {
        self.inner.get_user_by_email(email).await
    }

    async fn get_user_by_username(&self, username: &str) -> AuthResult<Option<Self::User>> {
        self.inner.get_user_by_username(username).await
    }

    async fn update_user(&self, id: &str, mut update: UpdateUser) -> AuthResult<Self::User> {
        hooked_update!(
            self,
            before_update_user,
            after_update_user,
            update_user,
            id,
            update
        )
    }

    async fn delete_user(&self, id: &str) -> AuthResult<()> {
        hooked_delete!(self, before_delete_user, after_delete_user, delete_user, id)
    }

    async fn list_users(&self, params: ListUsersParams) -> AuthResult<(Vec<Self::User>, usize)> {
        self.inner.list_users(params).await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> SessionOps for HookedDatabaseAdapter<DB> {
    type Session = DB::Session;

    async fn create_session(&self, mut session: CreateSession) -> AuthResult<Self::Session> {
        hooked_create!(
            self,
            before_create_session,
            after_create_session,
            create_session,
            session
        )
    }

    async fn get_session(&self, token: &str) -> AuthResult<Option<Self::Session>> {
        self.inner.get_session(token).await
    }

    async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<Self::Session>> {
        self.inner.get_user_sessions(user_id).await
    }

    async fn update_session_expiry(
        &self,
        token: &str,
        expires_at: DateTime<Utc>,
    ) -> AuthResult<()> {
        self.inner.update_session_expiry(token, expires_at).await
    }

    async fn delete_session(&self, token: &str) -> AuthResult<()> {
        hooked_delete!(
            self,
            before_delete_session,
            after_delete_session,
            delete_session,
            token
        )
    }

    async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()> {
        self.inner.delete_user_sessions(user_id).await
    }

    async fn delete_expired_sessions(&self) -> AuthResult<usize> {
        self.inner.delete_expired_sessions().await
    }

    async fn update_session_active_organization(
        &self,
        token: &str,
        organization_id: Option<&str>,
    ) -> AuthResult<Self::Session> {
        self.inner
            .update_session_active_organization(token, organization_id)
            .await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> AccountOps for HookedDatabaseAdapter<DB> {
    type Account = DB::Account;

    async fn create_account(&self, mut account: CreateAccount) -> AuthResult<Self::Account> {
        hooked_create!(
            self,
            before_create_account,
            after_create_account,
            create_account,
            account
        )
    }

    async fn get_account(
        &self,
        provider: &str,
        provider_account_id: &str,
    ) -> AuthResult<Option<Self::Account>> {
        self.inner.get_account(provider, provider_account_id).await
    }

    async fn get_user_accounts(&self, user_id: &str) -> AuthResult<Vec<Self::Account>> {
        self.inner.get_user_accounts(user_id).await
    }

    async fn update_account(
        &self,
        id: &str,
        mut update: UpdateAccount,
    ) -> AuthResult<Self::Account> {
        hooked_update!(
            self,
            before_update_account,
            after_update_account,
            update_account,
            id,
            update
        )
    }

    async fn delete_account(&self, id: &str) -> AuthResult<()> {
        hooked_delete!(
            self,
            before_delete_account,
            after_delete_account,
            delete_account,
            id
        )
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> VerificationOps for HookedDatabaseAdapter<DB> {
    type Verification = DB::Verification;

    async fn create_verification(
        &self,
        mut verification: CreateVerification,
    ) -> AuthResult<Self::Verification> {
        hooked_create!(
            self,
            before_create_verification,
            after_create_verification,
            create_verification,
            verification
        )
    }

    async fn get_verification(
        &self,
        identifier: &str,
        value: &str,
    ) -> AuthResult<Option<Self::Verification>> {
        self.inner.get_verification(identifier, value).await
    }

    async fn get_verification_by_value(
        &self,
        value: &str,
    ) -> AuthResult<Option<Self::Verification>> {
        self.inner.get_verification_by_value(value).await
    }

    async fn get_verification_by_identifier(
        &self,
        identifier: &str,
    ) -> AuthResult<Option<Self::Verification>> {
        self.inner.get_verification_by_identifier(identifier).await
    }

    async fn consume_verification(
        &self,
        identifier: &str,
        value: &str,
    ) -> AuthResult<Option<Self::Verification>> {
        self.inner.consume_verification(identifier, value).await
    }

    async fn delete_verification(&self, id: &str) -> AuthResult<()> {
        hooked_delete!(
            self,
            before_delete_verification,
            after_delete_verification,
            delete_verification,
            id
        )
    }

    async fn create_email_send_failure(&self, failure: CreateEmailSendFailure) -> AuthResult<()> {
        self.inner.create_email_send_failure(failure).await
    }

    async fn create_email_send_failure_record(
        &self,
        failure: CreateEmailSendFailure,
    ) -> AuthResult<EmailSendFailure> {
        self.inner.create_email_send_failure_record(failure).await
    }

    async fn create_sent_email(&self, sent: CreateSentEmail) -> AuthResult<()> {
        self.inner.create_sent_email(sent).await
    }

    async fn create_sent_email_record(&self, sent: CreateSentEmail) -> AuthResult<SentEmail> {
        self.inner.create_sent_email_record(sent).await
    }

    async fn list_sent_emails(&self, limit: usize, offset: usize) -> AuthResult<Vec<SentEmail>> {
        self.inner.list_sent_emails(limit, offset).await
    }

    async fn list_sent_emails_with_params(
        &self,
        params: ListSentEmailsParams,
    ) -> AuthResult<Vec<SentEmail>> {
        self.inner.list_sent_emails_with_params(params).await
    }

    async fn count_sent_emails(&self, params: ListSentEmailsParams) -> AuthResult<usize> {
        self.inner.count_sent_emails(params).await
    }

    async fn list_sent_emails_for_recipient(
        &self,
        recipient_email: &str,
        limit: usize,
        offset: usize,
    ) -> AuthResult<Vec<SentEmail>> {
        self.inner
            .list_sent_emails_for_recipient(recipient_email, limit, offset)
            .await
    }

    async fn get_sent_email_by_id(&self, id: &str) -> AuthResult<Option<SentEmail>> {
        self.inner.get_sent_email_by_id(id).await
    }

    async fn update_sent_email(&self, id: &str, update: UpdateSentEmail) -> AuthResult<SentEmail> {
        self.inner.update_sent_email(id, update).await
    }

    async fn delete_sent_email(&self, id: &str) -> AuthResult<()> {
        self.inner.delete_sent_email(id).await
    }

    async fn list_email_send_failures(
        &self,
        params: ListEmailSendFailuresParams,
    ) -> AuthResult<Vec<EmailSendFailure>> {
        self.inner.list_email_send_failures(params).await
    }

    async fn count_email_send_failures(
        &self,
        params: ListEmailSendFailuresParams,
    ) -> AuthResult<usize> {
        self.inner.count_email_send_failures(params).await
    }

    async fn get_email_send_failure_by_id(&self, id: &str) -> AuthResult<Option<EmailSendFailure>> {
        self.inner.get_email_send_failure_by_id(id).await
    }

    async fn update_email_send_failure(
        &self,
        id: &str,
        update: UpdateEmailSendFailure,
    ) -> AuthResult<EmailSendFailure> {
        self.inner.update_email_send_failure(id, update).await
    }

    async fn delete_email_send_failure(&self, id: &str) -> AuthResult<()> {
        self.inner.delete_email_send_failure(id).await
    }

    async fn get_active_email_template(
        &self,
        template_key: &str,
        locale: Option<&str>,
    ) -> AuthResult<Option<EmailTemplate>> {
        self.inner
            .get_active_email_template(template_key, locale)
            .await
    }

    async fn get_active_email_template_for_event(
        &self,
        event_type: &str,
        template_key: &str,
        locale: Option<&str>,
    ) -> AuthResult<Option<EmailTemplate>> {
        self.inner
            .get_active_email_template_for_event(event_type, template_key, locale)
            .await
    }

    async fn create_email_template(&self, input: CreateEmailTemplate) -> AuthResult<EmailTemplate> {
        self.inner.create_email_template(input).await
    }

    async fn list_email_templates(
        &self,
        params: ListEmailTemplatesParams,
    ) -> AuthResult<Vec<EmailTemplate>> {
        self.inner.list_email_templates(params).await
    }

    async fn get_email_template_by_id(&self, id: &str) -> AuthResult<Option<EmailTemplate>> {
        self.inner.get_email_template_by_id(id).await
    }

    async fn count_email_templates(&self, params: ListEmailTemplatesParams) -> AuthResult<usize> {
        self.inner.count_email_templates(params).await
    }

    async fn update_email_template(
        &self,
        id: &str,
        update: UpdateEmailTemplate,
    ) -> AuthResult<EmailTemplate> {
        self.inner.update_email_template(id, update).await
    }

    async fn delete_email_template(&self, id: &str) -> AuthResult<()> {
        self.inner.delete_email_template(id).await
    }

    async fn list_email_event_types(
        &self,
        params: ListEmailEventTypesParams,
    ) -> AuthResult<Vec<EmailEventType>> {
        self.inner.list_email_event_types(params).await
    }

    async fn count_email_event_types(
        &self,
        params: ListEmailEventTypesParams,
    ) -> AuthResult<usize> {
        self.inner.count_email_event_types(params).await
    }

    async fn get_email_event_type(&self, event_type: &str) -> AuthResult<Option<EmailEventType>> {
        self.inner.get_email_event_type(event_type).await
    }

    async fn create_admin_audit_log(&self, log: CreateAdminAuditLog) -> AuthResult<()> {
        self.inner.create_admin_audit_log(log).await
    }

    async fn list_admin_audit_logs(
        &self,
        limit: usize,
        offset: usize,
    ) -> AuthResult<Vec<AdminAuditLog>> {
        self.inner.list_admin_audit_logs(limit, offset).await
    }

    async fn list_admin_audit_logs_with_params(
        &self,
        params: ListAdminAuditLogsParams,
    ) -> AuthResult<Vec<AdminAuditLog>> {
        self.inner.list_admin_audit_logs_with_params(params).await
    }

    async fn count_admin_audit_logs(&self, params: ListAdminAuditLogsParams) -> AuthResult<usize> {
        self.inner.count_admin_audit_logs(params).await
    }

    async fn create_athena_client(&self, input: CreateAthenaClient) -> AuthResult<AthenaClient> {
        self.inner.create_athena_client(input).await
    }

    async fn list_athena_clients(&self) -> AuthResult<Vec<AthenaClient>> {
        self.inner.list_athena_clients().await
    }

    async fn create_oauth_client(&self, input: CreateOAuthClient) -> AuthResult<OAuthClient> {
        self.inner.create_oauth_client(input).await
    }

    async fn list_oauth_clients(&self) -> AuthResult<Vec<OAuthClient>> {
        self.inner.list_oauth_clients().await
    }

    async fn find_active_oauth_client_by_provider_and_redirect_uri(
        &self,
        provider: &str,
        redirect_uri: &str,
    ) -> AuthResult<Option<OAuthClient>> {
        self.inner
            .find_active_oauth_client_by_provider_and_redirect_uri(provider, redirect_uri)
            .await
    }

    async fn delete_expired_verifications(&self) -> AuthResult<usize> {
        self.inner.delete_expired_verifications().await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> OrganizationOps for HookedDatabaseAdapter<DB> {
    type Organization = DB::Organization;

    async fn create_organization(&self, org: CreateOrganization) -> AuthResult<Self::Organization> {
        self.inner.create_organization(org).await
    }

    async fn get_organization_by_id(&self, id: &str) -> AuthResult<Option<Self::Organization>> {
        self.inner.get_organization_by_id(id).await
    }

    async fn get_organization_by_slug(&self, slug: &str) -> AuthResult<Option<Self::Organization>> {
        self.inner.get_organization_by_slug(slug).await
    }

    async fn update_organization(
        &self,
        id: &str,
        update: UpdateOrganization,
    ) -> AuthResult<Self::Organization> {
        self.inner.update_organization(id, update).await
    }

    async fn delete_organization(&self, id: &str) -> AuthResult<()> {
        self.inner.delete_organization(id).await
    }

    async fn list_user_organizations(&self, user_id: &str) -> AuthResult<Vec<Self::Organization>> {
        self.inner.list_user_organizations(user_id).await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> MemberOps for HookedDatabaseAdapter<DB> {
    type Member = DB::Member;

    async fn create_member(&self, member: CreateMember) -> AuthResult<Self::Member> {
        self.inner.create_member(member).await
    }

    async fn get_member(
        &self,
        organization_id: &str,
        user_id: &str,
    ) -> AuthResult<Option<Self::Member>> {
        self.inner.get_member(organization_id, user_id).await
    }

    async fn get_member_by_id(&self, id: &str) -> AuthResult<Option<Self::Member>> {
        self.inner.get_member_by_id(id).await
    }

    async fn update_member_role(&self, member_id: &str, role: &str) -> AuthResult<Self::Member> {
        self.inner.update_member_role(member_id, role).await
    }

    async fn delete_member(&self, member_id: &str) -> AuthResult<()> {
        self.inner.delete_member(member_id).await
    }

    async fn list_organization_members(
        &self,
        organization_id: &str,
    ) -> AuthResult<Vec<Self::Member>> {
        self.inner.list_organization_members(organization_id).await
    }

    async fn count_organization_members(&self, organization_id: &str) -> AuthResult<usize> {
        self.inner.count_organization_members(organization_id).await
    }

    async fn count_organization_owners(&self, organization_id: &str) -> AuthResult<usize> {
        self.inner.count_organization_owners(organization_id).await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> InvitationOps for HookedDatabaseAdapter<DB> {
    type Invitation = DB::Invitation;

    async fn create_invitation(
        &self,
        invitation: CreateInvitation,
    ) -> AuthResult<Self::Invitation> {
        self.inner.create_invitation(invitation).await
    }

    async fn get_invitation_by_id(&self, id: &str) -> AuthResult<Option<Self::Invitation>> {
        self.inner.get_invitation_by_id(id).await
    }

    async fn get_pending_invitation(
        &self,
        organization_id: &str,
        email: &str,
    ) -> AuthResult<Option<Self::Invitation>> {
        self.inner
            .get_pending_invitation(organization_id, email)
            .await
    }

    async fn update_invitation_status(
        &self,
        id: &str,
        status: InvitationStatus,
    ) -> AuthResult<Self::Invitation> {
        self.inner.update_invitation_status(id, status).await
    }

    async fn list_organization_invitations(
        &self,
        organization_id: &str,
    ) -> AuthResult<Vec<Self::Invitation>> {
        self.inner
            .list_organization_invitations(organization_id)
            .await
    }

    async fn list_user_invitations(&self, email: &str) -> AuthResult<Vec<Self::Invitation>> {
        self.inner.list_user_invitations(email).await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> TwoFactorOps for HookedDatabaseAdapter<DB> {
    type TwoFactor = DB::TwoFactor;

    async fn create_two_factor(&self, two_factor: CreateTwoFactor) -> AuthResult<Self::TwoFactor> {
        self.inner.create_two_factor(two_factor).await
    }

    async fn get_two_factor_by_user_id(
        &self,
        user_id: &str,
    ) -> AuthResult<Option<Self::TwoFactor>> {
        self.inner.get_two_factor_by_user_id(user_id).await
    }

    async fn update_two_factor_backup_codes(
        &self,
        user_id: &str,
        backup_codes: &str,
    ) -> AuthResult<Self::TwoFactor> {
        self.inner
            .update_two_factor_backup_codes(user_id, backup_codes)
            .await
    }

    async fn delete_two_factor(&self, user_id: &str) -> AuthResult<()> {
        self.inner.delete_two_factor(user_id).await
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> ApiKeyOps for HookedDatabaseAdapter<DB> {
    type ApiKey = DB::ApiKey;

    async fn create_api_key(&self, input: CreateApiKey) -> AuthResult<Self::ApiKey> {
        self.inner.create_api_key(input).await
    }

    async fn get_api_key_by_id(&self, id: &str) -> AuthResult<Option<Self::ApiKey>> {
        self.inner.get_api_key_by_id(id).await
    }

    async fn get_api_key_by_hash(&self, hash: &str) -> AuthResult<Option<Self::ApiKey>> {
        self.inner.get_api_key_by_hash(hash).await
    }

    async fn list_api_keys_by_user(&self, user_id: &str) -> AuthResult<Vec<Self::ApiKey>> {
        self.inner.list_api_keys_by_user(user_id).await
    }

    async fn update_api_key(&self, id: &str, update: UpdateApiKey) -> AuthResult<Self::ApiKey> {
        self.inner.update_api_key(id, update).await
    }

    async fn delete_api_key(&self, id: &str) -> AuthResult<()> {
        self.inner.delete_api_key(id).await
    }

    async fn delete_expired_api_keys(&self) -> AuthResult<usize> {
        self.inner.delete_expired_api_keys().await
    }
}

#[cfg(test)]
#[path = "__tests__/hooks.rs"]
mod tests;
