use std::sync::Arc;

use async_trait::async_trait;

use crate::adapters::DatabaseAdapter;
use crate::error::AuthResult;
use crate::types::CreateSentEmail;

use super::{DEFAULT_EMAIL_TRANSPORT_ID, EmailProvider};

/// Wraps an [`EmailProvider`], inserts a row into `emails` after each successful send.
pub struct DatabaseLoggingEmailProvider<DB: DatabaseAdapter> {
    db: Arc<DB>,
    inner: Arc<dyn EmailProvider>,
    from_address: String,
    from_name: Option<String>,
    provider_tag: String,
}

impl<DB: DatabaseAdapter> DatabaseLoggingEmailProvider<DB> {
    pub fn new(
        db: Arc<DB>,
        inner: Arc<dyn EmailProvider>,
        from_address: String,
        from_name: Option<String>,
        provider_tag: Option<String>,
    ) -> Self {
        Self {
            db,
            inner,
            from_address,
            from_name,
            provider_tag: provider_tag.unwrap_or_else(|| DEFAULT_EMAIL_TRANSPORT_ID.to_string()),
        }
    }
}

#[async_trait]
impl<DB: DatabaseAdapter> EmailProvider for DatabaseLoggingEmailProvider<DB> {
    async fn send(&self, to: &str, subject: &str, html: &str, text: &str) -> AuthResult<()> {
        self.inner.send(to, subject, html, text).await?;

        tracing::info!(
            recipient = %to,
            subject = %subject,
            provider = %self.provider_tag,
            "transactional email accepted by provider (audit row next)"
        );

        let row: CreateSentEmail = CreateSentEmail {
            recipient_email: to.to_string(),
            subject: subject.to_string(),
            from_address: self.from_address.clone(),
            from_name: self.from_name.clone(),
            text_body: (!text.is_empty()).then(|| text.to_string()),
            html_body: (!html.is_empty()).then(|| html.to_string()),
            provider: self.provider_tag.clone(),
            flow: Some("auth.transactional".to_string()),
            metadata: None,
        };

        if let Err(e) = self.db.create_sent_email(row).await {
            tracing::error!(
                error = %e,
                recipient = %to,
                "failed to persist sent email audit row"
            );
        }

        Ok(())
    }
}
