use crate::{config::EmailConfig, core::urls::ApiUrls, shared::state::AppState};
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::{Html, IntoResponse, Response},
    Json,
};
use axum::{
    routing::{delete, get, post},
    Router,
};
use base64::{engine::general_purpose, Engine as _};
use chrono::{DateTime, Utc};
use diesel::prelude::*;
use diesel::sql_types::{Bool, Integer, Nullable, Text, Timestamptz, Uuid as DieselUuid};
use imap::types::Seq;
use lettre::{transport::smtp::authentication::Credentials, Message, SmtpTransport, Transport};
use log::{debug, info, warn};
use mailparse::{parse_mail, MailHeaderMap};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use uuid::Uuid;

#[derive(Debug, QueryableByName)]
pub struct EmailAccountBasicRow {
    #[diesel(sql_type = DieselUuid)]
    pub id: Uuid,
    #[diesel(sql_type = Text)]
    pub email: String,
    #[diesel(sql_type = Nullable<Text>)]
    pub display_name: Option<String>,
    #[diesel(sql_type = Bool)]
    pub is_primary: bool,
}

#[derive(Debug, QueryableByName)]
pub struct ImapCredentialsRow {
    #[diesel(sql_type = Text)]
    pub imap_server: String,
    #[diesel(sql_type = Integer)]
    pub imap_port: i32,
    #[diesel(sql_type = Text)]
    pub username: String,
    #[diesel(sql_type = Text)]
    pub password_encrypted: String,
}

#[derive(Debug, QueryableByName)]
pub struct SmtpCredentialsRow {
    #[diesel(sql_type = Text)]
    pub email: String,
    #[diesel(sql_type = Text)]
    pub display_name: String,
    #[diesel(sql_type = Integer)]
    pub smtp_port: i32,
    #[diesel(sql_type = Text)]
    pub smtp_server: String,
    #[diesel(sql_type = Text)]
    pub username: String,
    #[diesel(sql_type = Text)]
    pub password_encrypted: String,
}

#[derive(Debug, QueryableByName)]
pub struct EmailSearchRow {
    #[diesel(sql_type = Text)]
    pub id: String,
    #[diesel(sql_type = Text)]
    pub subject: String,
    #[diesel(sql_type = Text)]
    pub from_address: String,
    #[diesel(sql_type = Text)]
    pub to_addresses: String,
    #[diesel(sql_type = Nullable<Text>)]
    pub body_text: Option<String>,
    #[diesel(sql_type = Timestamptz)]
    pub received_at: DateTime<Utc>,
}

pub mod stalwart_client;
pub mod stalwart_sync;
pub mod vectordb;

fn extract_user_from_session(_state: &Arc<AppState>) -> Result<Uuid, String> {
    Ok(Uuid::new_v4())
}

pub fn configure() -> Router<Arc<AppState>> {
    Router::new()
        .route(ApiUrls::EMAIL_ACCOUNTS, get(list_email_accounts))
        .route(
            &format!("{}/add", ApiUrls::EMAIL_ACCOUNTS),
            post(add_email_account),
        )
        .route(
            &ApiUrls::EMAIL_ACCOUNT_BY_ID.replace(":id", "{account_id}"),
            axum::routing::delete(delete_email_account),
        )
        .route(ApiUrls::EMAIL_LIST, post(list_emails))
        .route(ApiUrls::EMAIL_SEND, post(send_email))
        .route(ApiUrls::EMAIL_DRAFT, post(save_draft))
        .route(
            &ApiUrls::EMAIL_FOLDERS.replace(":account_id", "{account_id}"),
            get(list_folders),
        )
        .route(ApiUrls::EMAIL_LATEST, get(get_latest_email))
        .route(
            &ApiUrls::EMAIL_GET.replace(":campaign_id", "{campaign_id}"),
            get(get_email),
        )
        .route(
            &ApiUrls::EMAIL_CLICK
                .replace(":campaign_id", "{campaign_id}")
                .replace(":email", "{email}"),
            post(track_click),
        )
        .route(
            "/api/email/tracking/pixel/{tracking_id}",
            get(serve_tracking_pixel),
        )
        .route(
            "/api/email/tracking/status/{tracking_id}",
            get(get_tracking_status),
        )
        .route("/api/email/tracking/list", get(list_sent_emails_tracking))
        .route("/api/email/tracking/stats", get(get_tracking_stats))
        .route("/ui/email/accounts", get(list_email_accounts_htmx))
        .route("/ui/email/list", get(list_emails_htmx))
        .route("/ui/email/folders", get(list_folders_htmx))
        .route("/ui/email/compose", get(compose_email_htmx))
        .route("/ui/email/:id", get(get_email_content_htmx))
        .route("/ui/email/:id/delete", delete(delete_email_htmx))
        .route("/ui/email/labels", get(list_labels_htmx))
        .route("/ui/email/templates", get(list_templates_htmx))
        .route("/ui/email/signatures", get(list_signatures_htmx))
        .route("/ui/email/rules", get(list_rules_htmx))
        .route("/ui/email/search", get(search_emails_htmx))
        .route("/ui/email/auto-responder", post(save_auto_responder))
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SaveDraftRequest {
    pub account_id: String,
    pub to: String,
    pub cc: Option<String>,
    pub bcc: Option<String>,
    pub subject: String,
    pub body: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SentEmailTracking {
    pub id: String,
    pub tracking_id: String,
    pub bot_id: String,
    pub account_id: String,
    pub from_email: String,
    pub to_email: String,
    pub cc: Option<String>,
    pub bcc: Option<String>,
    pub subject: String,
    pub sent_at: DateTime<Utc>,
    pub read_at: Option<DateTime<Utc>>,
    pub read_count: i32,
    pub first_read_ip: Option<String>,
    pub last_read_ip: Option<String>,
    pub user_agent: Option<String>,
    pub is_read: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackingStatusResponse {
    pub tracking_id: String,
    pub to_email: String,
    pub subject: String,
    pub sent_at: String,
    pub is_read: bool,
    pub read_at: Option<String>,
    pub read_count: i32,
}

#[derive(Debug, Deserialize)]
pub struct TrackingPixelQuery {
    pub t: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct ListTrackingQuery {
    pub account_id: Option<String>,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
    pub filter: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct TrackingStatsResponse {
    pub total_sent: i64,
    pub total_read: i64,
    pub read_rate: f64,
    pub avg_time_to_read_hours: Option<f64>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EmailAccountRequest {
    pub email: String,
    pub display_name: Option<String>,
    pub imap_server: String,
    pub imap_port: u16,
    pub smtp_server: String,
    pub smtp_port: u16,
    pub username: String,
    pub password: String,
    pub is_primary: bool,
}

#[derive(Debug, Serialize)]
pub struct EmailAccountResponse {
    pub id: String,
    pub email: String,
    pub display_name: Option<String>,
    pub imap_server: String,
    pub imap_port: u16,
    pub smtp_server: String,
    pub smtp_port: u16,
    pub is_primary: bool,
    pub is_active: bool,
    pub created_at: String,
}

#[derive(Debug, Serialize)]
pub struct EmailResponse {
    pub id: String,
    pub from_name: String,
    pub from_email: String,
    pub to: String,
    pub subject: String,
    pub preview: String,
    pub body: String,
    pub date: String,
    pub time: String,
    pub read: bool,
    pub folder: String,
    pub has_attachments: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EmailRequest {
    pub to: String,
    pub subject: String,
    pub body: String,
    pub cc: Option<String>,
    pub bcc: Option<String>,
    pub attachments: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SendEmailRequest {
    pub account_id: String,
    pub to: String,
    pub cc: Option<String>,
    pub bcc: Option<String>,
    pub subject: String,
    pub body: String,
    pub is_html: bool,
}

#[derive(Debug, Serialize)]
pub struct SaveDraftResponse {
    pub success: bool,
    pub draft_id: Option<String>,
    pub message: String,
}

#[derive(Debug, Deserialize)]
pub struct ListEmailsRequest {
    pub account_id: String,
    pub folder: Option<String>,
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

#[derive(Debug, Deserialize)]
pub struct MarkEmailRequest {
    pub account_id: String,
    pub email_id: String,
    pub read: bool,
}

#[derive(Debug, Deserialize)]
pub struct DeleteEmailRequest {
    pub account_id: String,
    pub email_id: String,
}

#[derive(Debug, Serialize)]
pub struct FolderInfo {
    pub name: String,
    pub path: String,
    pub unread_count: i32,
    pub total_count: i32,
}

#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: Option<String>,
}

pub struct EmailError(String);

impl IntoResponse for EmailError {
    fn into_response(self) -> Response {
        (StatusCode::INTERNAL_SERVER_ERROR, self.0).into_response()
    }
}

impl From<String> for EmailError {
    fn from(s: String) -> Self {
        Self(s)
    }
}

fn parse_from_field(from: &str) -> (String, String) {
    if let Some(start) = from.find('<') {
        if let Some(end) = from.find('>') {
            let name = from[..start].trim().trim_matches('"').to_string();
            let email = from[start + 1..end].to_string();
            return (name, email);
        }
    }
    (String::new(), from.to_string())
}

fn format_email_time(date_str: &str) -> String {
    if date_str.is_empty() {
        return "Unknown".to_string();
    }

    date_str
        .split_whitespace()
        .take(4)
        .collect::<Vec<_>>()
        .join(" ")
}

fn encrypt_password(password: &str) -> String {
    general_purpose::STANDARD.encode(password.as_bytes())
}

fn decrypt_password(encrypted: &str) -> Result<String, String> {
    general_purpose::STANDARD
        .decode(encrypted)
        .map_err(|e| format!("Decryption failed: {e}"))
        .and_then(|bytes| {
            String::from_utf8(bytes).map_err(|e| format!("UTF-8 conversion failed: {e}"))
        })
}

pub async fn add_email_account(
    State(state): State<Arc<AppState>>,
    Json(request): Json<EmailAccountRequest>,
) -> Result<Json<ApiResponse<EmailAccountResponse>>, EmailError> {
    let Ok(current_user_id) = extract_user_from_session(&state) else {
        return Err(EmailError("Authentication required".to_string()));
    };

    let account_id = Uuid::new_v4();
    let encrypted_password = encrypt_password(&request.password);

    let resp_email = request.email.clone();
    let resp_display_name = request.display_name.clone();
    let resp_imap_server = request.imap_server.clone();
    let resp_imap_port = request.imap_port;
    let resp_smtp_server = request.smtp_server.clone();
    let resp_smtp_port = request.smtp_port;
    let resp_is_primary = request.is_primary;

    let conn = state.conn.clone();
    tokio::task::spawn_blocking(move || {
        use crate::shared::models::schema::user_email_accounts::dsl::{is_primary, user_email_accounts, user_id};
        let mut db_conn = conn.get().map_err(|e| format!("DB connection error: {e}"))?;


        if request.is_primary {
            diesel::update(user_email_accounts.filter(user_id.eq(&current_user_id)))
                .set(is_primary.eq(false))
                .execute(&mut db_conn)
                .ok();
        }

        diesel::sql_query(
            "INSERT INTO user_email_accounts
            (id, user_id, email, display_name, imap_server, imap_port, smtp_server, smtp_port, username, password_encrypted, is_primary, is_active)
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)"
        )
        .bind::<diesel::sql_types::Uuid, _>(account_id)
        .bind::<diesel::sql_types::Uuid, _>(current_user_id)
        .bind::<diesel::sql_types::Text, _>(&request.email)
        .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(request.display_name.as_ref())
        .bind::<diesel::sql_types::Text, _>(&request.imap_server)
        .bind::<diesel::sql_types::Integer, _>(i32::from(request.imap_port))
        .bind::<diesel::sql_types::Text, _>(&request.smtp_server)
        .bind::<diesel::sql_types::Integer, _>(i32::from(request.smtp_port))
        .bind::<diesel::sql_types::Text, _>(&request.username)
        .bind::<diesel::sql_types::Text, _>(&encrypted_password)
        .bind::<diesel::sql_types::Bool, _>(request.is_primary)
        .bind::<diesel::sql_types::Bool, _>(true)
        .execute(&mut db_conn)
        .map_err(|e| format!("Failed to insert account: {e}"))?;

        Ok::<_, String>(account_id)
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    Ok(Json(ApiResponse {
        success: true,
        data: Some(EmailAccountResponse {
            id: account_id.to_string(),
            email: resp_email,
            display_name: resp_display_name,
            imap_server: resp_imap_server,
            imap_port: resp_imap_port,
            smtp_server: resp_smtp_server,
            smtp_port: resp_smtp_port,
            is_primary: resp_is_primary,
            is_active: true,
            created_at: chrono::Utc::now().to_rfc3339(),
        }),
        message: Some("Email account added successfully".to_string()),
    }))
}

pub async fn list_email_accounts_htmx(State(state): State<Arc<AppState>>) -> impl IntoResponse {
    let Ok(user_id) = extract_user_from_session(&state) else {
        return axum::response::Html(r#"
            <div class="account-item" onclick="document.getElementById('add-account-modal').showModal()">
                <span>+ Add email account</span>
            </div>
        "#.to_string());
    };

    let conn = state.conn.clone();
    let accounts = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn.get().map_err(|e| format!("DB connection error: {e}"))?;

        diesel::sql_query(
            "SELECT id, email, display_name, is_primary FROM user_email_accounts WHERE user_id = $1 AND is_active = true ORDER BY is_primary DESC"
        )
        .bind::<diesel::sql_types::Uuid, _>(user_id)
        .load::<EmailAccountBasicRow>(&mut db_conn)
        .map_err(|e| format!("Query failed: {e}"))
    })
    .await
    .ok()
    .and_then(Result::ok)
    .unwrap_or_default();

    if accounts.is_empty() {
        return axum::response::Html(r#"
            <div class="account-item" onclick="document.getElementById('add-account-modal').showModal()">
                <span>+ Add email account</span>
            </div>
        "#.to_string());
    }

    let mut html = String::new();
    for account in accounts {
        let name = account
            .display_name
            .clone()
            .unwrap_or_else(|| account.email.clone());
        let primary_badge = if account.is_primary {
            r#"<span class="badge">Primary</span>"#
        } else {
            ""
        };
        use std::fmt::Write;
        let _ = write!(
            html,
            r#"<div class="account-item" data-account-id="{}">
                <span>{}</span>
                {}
            </div>"#,
            account.id, name, primary_badge
        );
    }

    axum::response::Html(html)
}

pub async fn list_email_accounts(
    State(state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<Vec<EmailAccountResponse>>>, EmailError> {
    let Ok(current_user_id) = extract_user_from_session(&state) else {
        return Err(EmailError("Authentication required".to_string()));
    };

    let conn = state.conn.clone();
    let accounts = tokio::task::spawn_blocking(move || {
        use crate::shared::models::schema::user_email_accounts::dsl::{
            created_at, display_name, email, id, imap_port, imap_server, is_active, is_primary,
            smtp_port, smtp_server, user_email_accounts, user_id,
        };
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {e}"))?;

        let results = user_email_accounts
            .filter(user_id.eq(current_user_id))
            .filter(is_active.eq(true))
            .order((is_primary.desc(), created_at.desc()))
            .select((
                id,
                email,
                display_name,
                imap_server,
                imap_port,
                smtp_server,
                smtp_port,
                is_primary,
                is_active,
                created_at,
            ))
            .load::<(
                Uuid,
                String,
                Option<String>,
                String,
                i32,
                String,
                i32,
                bool,
                bool,
                chrono::DateTime<chrono::Utc>,
            )>(&mut db_conn)
            .map_err(|e| format!("Query failed: {e}"))?;

        Ok::<_, String>(results)
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    let account_list: Vec<EmailAccountResponse> = accounts
        .into_iter()
        .map(
            |(
                acc_id,
                acc_email,
                acc_display_name,
                acc_imap_server,
                acc_imap_port,
                acc_smtp_server,
                acc_smtp_port,
                acc_is_primary,
                acc_is_active,
                acc_created_at,
            )| {
                EmailAccountResponse {
                    id: acc_id.to_string(),
                    email: acc_email,
                    display_name: acc_display_name,
                    imap_server: acc_imap_server,
                    imap_port: acc_imap_port as u16,
                    smtp_server: acc_smtp_server,
                    smtp_port: acc_smtp_port as u16,
                    is_primary: acc_is_primary,
                    is_active: acc_is_active,
                    created_at: acc_created_at.to_rfc3339(),
                }
            },
        )
        .collect();

    Ok(Json(ApiResponse {
        success: true,
        data: Some(account_list),
        message: None,
    }))
}

pub async fn delete_email_account(
    State(state): State<Arc<AppState>>,
    Path(account_id): Path<String>,
) -> Result<Json<ApiResponse<()>>, EmailError> {
    let account_uuid =
        Uuid::parse_str(&account_id).map_err(|_| EmailError("Invalid account ID".to_string()))?;

    let conn = state.conn.clone();
    tokio::task::spawn_blocking(move || {
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {e}"))?;

        diesel::sql_query("UPDATE user_email_accounts SET is_active = false WHERE id = $1")
            .bind::<diesel::sql_types::Uuid, _>(account_uuid)
            .execute(&mut db_conn)
            .map_err(|e| format!("Failed to delete account: {e}"))?;

        Ok::<_, String>(())
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    Ok(Json(ApiResponse {
        success: true,
        data: Some(()),
        message: Some("Email account deleted".to_string()),
    }))
}

pub async fn list_emails(
    State(state): State<Arc<AppState>>,
    Json(request): Json<ListEmailsRequest>,
) -> Result<Json<ApiResponse<Vec<EmailResponse>>>, EmailError> {
    let account_uuid = Uuid::parse_str(&request.account_id)
        .map_err(|_| EmailError("Invalid account ID".to_string()))?;

    let conn = state.conn.clone();
    let account_info = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn.get().map_err(|e| format!("DB connection error: {e}"))?;

        let result: ImapCredentialsRow = diesel::sql_query(
            "SELECT imap_server, imap_port, username, password_encrypted FROM user_email_accounts WHERE id = $1 AND is_active = true"
        )
        .bind::<diesel::sql_types::Uuid, _>(account_uuid)
        .get_result(&mut db_conn)
        .map_err(|e| format!("Account not found: {e}"))?;

        Ok::<_, String>(result)
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    let (imap_server, imap_port, username, encrypted_password) = (
        account_info.imap_server,
        account_info.imap_port,
        account_info.username,
        account_info.password_encrypted,
    );
    let password = decrypt_password(&encrypted_password).map_err(EmailError)?;

    let client = imap::ClientBuilder::new(imap_server.as_str(), imap_port as u16)
        .connect()
        .map_err(|e| EmailError(format!("Failed to connect to IMAP: {e:?}")))?;

    let mut session = client
        .login(&username, &password)
        .map_err(|e| EmailError(format!("Login failed: {e:?}")))?;

    let folder = request.folder.unwrap_or_else(|| "INBOX".to_string());
    session
        .select(&folder)
        .map_err(|e| EmailError(format!("Failed to select folder: {e:?}")))?;

    let messages = session
        .search("ALL")
        .map_err(|e| EmailError(format!("Failed to search emails: {e:?}")))?;

    let mut email_list = Vec::new();
    let limit = request.limit.unwrap_or(50);
    let offset = request.offset.unwrap_or(0);

    let mut recent_messages: Vec<Seq> = messages.iter().copied().collect();
    recent_messages.sort_by(|a, b| b.cmp(a));
    let recent_messages: Vec<Seq> = recent_messages
        .into_iter()
        .skip(offset)
        .take(limit)
        .collect();

    for seq in recent_messages {
        let fetch_result = session.fetch(seq.to_string(), "RFC822");
        let messages =
            fetch_result.map_err(|e| EmailError(format!("Failed to fetch email: {e:?}")))?;

        for msg in messages.iter() {
            let body = msg
                .body()
                .ok_or_else(|| EmailError("No body found".to_string()))?;

            let parsed = parse_mail(body)
                .map_err(|e| EmailError(format!("Failed to parse email: {e:?}")))?;

            let headers = parsed.get_headers();
            let subject = headers.get_first_value("Subject").unwrap_or_default();
            let from = headers.get_first_value("From").unwrap_or_default();
            let to = headers.get_first_value("To").unwrap_or_default();
            let date = headers.get_first_value("Date").unwrap_or_default();

            let body_text = parsed
                .subparts
                .iter()
                .find(|p| p.ctype.mimetype == "text/plain")
                .map_or_else(
                    || parsed.get_body().unwrap_or_default(),
                    |body_part| body_part.get_body().unwrap_or_default(),
                );

            let body_html = parsed
                .subparts
                .iter()
                .find(|p| p.ctype.mimetype == "text/html")
                .map_or_else(String::new, |body_part| {
                    body_part.get_body().unwrap_or_default()
                });

            let preview = body_text.lines().take(3).collect::<Vec<_>>().join(" ");
            let preview_truncated = if preview.len() > 150 {
                format!("{}...", &preview[..150])
            } else {
                preview
            };

            let (from_name, from_email) = parse_from_field(&from);
            let has_attachments = parsed.subparts.iter().any(|p| {
                p.get_content_disposition().disposition == mailparse::DispositionType::Attachment
            });

            email_list.push(EmailResponse {
                id: seq.to_string(),
                from_name,
                from_email,
                to,
                subject,
                preview: preview_truncated,
                body: if body_html.is_empty() {
                    body_text
                } else {
                    body_html
                },
                date: format_email_time(&date),
                time: format_email_time(&date),
                read: false,
                folder: folder.clone(),
                has_attachments,
            });
        }
    }

    session.logout().ok();

    Ok(Json(ApiResponse {
        success: true,
        data: Some(email_list),
        message: None,
    }))
}

pub async fn send_email(
    State(state): State<Arc<AppState>>,
    Json(request): Json<SendEmailRequest>,
) -> Result<Json<ApiResponse<()>>, EmailError> {
    let account_uuid = Uuid::parse_str(&request.account_id)
        .map_err(|_| EmailError("Invalid account ID".to_string()))?;

    let conn = state.conn.clone();
    let account_info = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {e}"))?;

        let result: SmtpCredentialsRow = diesel::sql_query(
            "SELECT email, display_name, smtp_port, smtp_server, username, password_encrypted
            FROM user_email_accounts WHERE id = $1 AND is_active = true",
        )
        .bind::<diesel::sql_types::Uuid, _>(account_uuid)
        .get_result(&mut db_conn)
        .map_err(|e| format!("Account not found: {e}"))?;

        Ok::<_, String>(result)
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    let (from_email, display_name, smtp_port, smtp_server, username, encrypted_password) = (
        account_info.email,
        account_info.display_name,
        account_info.smtp_port,
        account_info.smtp_server,
        account_info.username,
        account_info.password_encrypted,
    );
    let password = decrypt_password(&encrypted_password).map_err(EmailError)?;

    let from_addr = if display_name.is_empty() {
        from_email.clone()
    } else {
        format!("{display_name} <{from_email}>")
    };

    let pixel_enabled = is_tracking_pixel_enabled(&state, None);
    let tracking_id = Uuid::new_v4();

    let final_body = if pixel_enabled && request.is_html {
        inject_tracking_pixel(&request.body, &tracking_id.to_string(), &state)
    } else {
        request.body.clone()
    };

    let mut email_builder = Message::builder()
        .from(
            from_addr
                .parse()
                .map_err(|e| EmailError(format!("Invalid from address: {e}")))?,
        )
        .to(request
            .to
            .parse()
            .map_err(|e| EmailError(format!("Invalid to address: {e}")))?)
        .subject(request.subject.clone());

    if let Some(ref cc) = request.cc {
        email_builder = email_builder.cc(cc
            .parse()
            .map_err(|e| EmailError(format!("Invalid cc address: {e}")))?);
    }

    if let Some(ref bcc) = request.bcc {
        email_builder = email_builder.bcc(
            bcc.parse()
                .map_err(|e| EmailError(format!("Invalid bcc address: {e}")))?,
        );
    }

    let email = email_builder
        .body(final_body)
        .map_err(|e| EmailError(format!("Failed to build email: {e}")))?;

    let creds = Credentials::new(username, password);
    let mailer = SmtpTransport::relay(&smtp_server)
        .map_err(|e| EmailError(format!("Failed to create SMTP transport: {e}")))?
        .port(u16::try_from(smtp_port).unwrap_or(587))
        .credentials(creds)
        .build();

    mailer
        .send(&email)
        .map_err(|e| EmailError(format!("Failed to send email: {e}")))?;

    if pixel_enabled {
        let conn = state.conn.clone();
        let to_email = request.to.clone();
        let subject = request.subject.clone();
        let cc_clone = request.cc.clone();
        let bcc_clone = request.bcc.clone();

        let _ = tokio::task::spawn_blocking(move || {
            save_email_tracking_record(
                conn,
                tracking_id,
                account_uuid,
                Uuid::nil(),
                &from_email,
                &to_email,
                cc_clone.as_deref(),
                bcc_clone.as_deref(),
                &subject,
            )
        })
        .await;
    }

    info!("Email sent successfully from account {account_uuid} with tracking_id {tracking_id}");

    Ok(Json(ApiResponse {
        success: true,
        data: Some(()),
        message: Some("Email sent successfully".to_string()),
    }))
}

pub async fn save_draft(
    State(state): State<Arc<AppState>>,
    Json(request): Json<SaveDraftRequest>,
) -> Result<Json<SaveDraftResponse>, EmailError> {
    let account_uuid = Uuid::parse_str(&request.account_id)
        .map_err(|_| EmailError("Invalid account ID".to_string()))?;

    let Ok(user_id) = extract_user_from_session(&state) else {
        return Err(EmailError("Authentication required".to_string()));
    };
    let draft_id = Uuid::new_v4();

    let conn = state.conn.clone();
    tokio::task::spawn_blocking(move || {
        let mut db_conn = conn.get().map_err(|e| format!("DB connection error: {e}"))?;

        diesel::sql_query(
            "INSERT INTO email_drafts (id, user_id, account_id, to_address, cc_address, bcc_address, subject, body)
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8)"
        )
        .bind::<diesel::sql_types::Uuid, _>(draft_id)
        .bind::<diesel::sql_types::Uuid, _>(user_id)
        .bind::<diesel::sql_types::Uuid, _>(account_uuid)
        .bind::<diesel::sql_types::Text, _>(&request.to)
        .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(request.cc.as_ref())
        .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(request.bcc.as_ref())
        .bind::<diesel::sql_types::Text, _>(&request.subject)
        .bind::<diesel::sql_types::Text, _>(&request.body)
        .execute(&mut db_conn)
        .map_err(|e| format!("Failed to save draft: {e}"))?;

        Ok::<_, String>(())
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    Ok(Json(SaveDraftResponse {
        success: true,
        draft_id: Some(draft_id.to_string()),
        message: "Draft saved successfully".to_string(),
    }))
}

pub async fn list_folders(
    State(state): State<Arc<AppState>>,
    Path(account_id): Path<String>,
) -> Result<Json<ApiResponse<Vec<FolderInfo>>>, EmailError> {
    let account_uuid =
        Uuid::parse_str(&account_id).map_err(|_| EmailError("Invalid account ID".to_string()))?;

    let conn = state.conn.clone();
    let account_info = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn.get().map_err(|e| format!("DB connection error: {e}"))?;

        let result: ImapCredentialsRow = diesel::sql_query(
            "SELECT imap_server, imap_port, username, password_encrypted FROM user_email_accounts WHERE id = $1 AND is_active = true"
        )
        .bind::<diesel::sql_types::Uuid, _>(account_uuid)
        .get_result(&mut db_conn)
        .map_err(|e| format!("Account not found: {e}"))?;

        Ok::<_, String>(result)
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    let (imap_server, imap_port, username, encrypted_password) = (
        account_info.imap_server,
        account_info.imap_port,
        account_info.username,
        account_info.password_encrypted,
    );
    let password = decrypt_password(&encrypted_password).map_err(EmailError)?;

    let client = imap::ClientBuilder::new(imap_server.as_str(), imap_port as u16)
        .connect()
        .map_err(|e| EmailError(format!("Failed to connect to IMAP: {e:?}")))?;

    let mut session = client
        .login(&username, &password)
        .map_err(|e| EmailError(format!("Login failed: {e:?}")))?;

    let folders = session
        .list(None, Some("*"))
        .map_err(|e| EmailError(format!("Failed to list folders: {e:?}")))?;

    let folder_list: Vec<FolderInfo> = folders
        .iter()
        .map(|f| FolderInfo {
            name: f.name().to_string(),
            path: f.name().to_string(),
            unread_count: 0,
            total_count: 0,
        })
        .collect();

    session.logout().ok();

    Ok(Json(ApiResponse {
        success: true,
        data: Some(folder_list),
        message: None,
    }))
}

pub fn get_latest_email_from(
    State(_state): State<Arc<AppState>>,
    Json(_request): Json<serde_json::Value>,
) -> Result<Json<serde_json::Value>, EmailError> {
    Ok(Json(serde_json::json!({
        "success": false,
        "message": "Please use the new /api/email/list endpoint with account_id"
    })))
}

pub fn save_click(
    Path((campaign_id, email)): Path<(String, String)>,
    State(_state): State<Arc<AppState>>,
) -> impl IntoResponse {
    info!(
        "Click tracked - Campaign: {}, Email: {}",
        campaign_id, email
    );

    let pixel: Vec<u8> = vec![
        0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xFF, 0xFF,
        0xFF, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3B,
    ];

    (StatusCode::OK, [("content-type", "image/gif")], pixel)
}

const TRACKING_PIXEL: [u8; 43] = [
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
    0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3B,
];

fn is_tracking_pixel_enabled(state: &Arc<AppState>, bot_id: Option<Uuid>) -> bool {
    let config_manager = crate::core::config::ConfigManager::new(state.conn.clone());
    let bot_id = bot_id.unwrap_or(Uuid::nil());

    config_manager
        .get_config(&bot_id, "email-read-pixel", Some("false"))
        .map(|v| v.to_lowercase() == "true")
        .unwrap_or(false)
}

fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc<AppState>) -> String {
    let config_manager = crate::core::config::ConfigManager::new(state.conn.clone());
    let base_url = config_manager
        .get_config(&Uuid::nil(), "server-url", Some("http://localhost:8080"))
        .unwrap_or_else(|_| "http://localhost:8080".to_string());

    let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id);
    let pixel_html = format!(
        r#"<img src="{}" width="1" height="1" style="display:none;visibility:hidden;width:1px;height:1px;border:0;" alt="" />"#,
        pixel_url
    );

    if html_body.to_lowercase().contains("</body>") {
        html_body
            .replace("</body>", &format!("{}</body>", pixel_html))
            .replace("</BODY>", &format!("{}</BODY>", pixel_html))
    } else {
        format!("{}{}", html_body, pixel_html)
    }
}

fn save_email_tracking_record(
    conn: crate::shared::utils::DbPool,
    tracking_id: Uuid,
    account_id: Uuid,
    bot_id: Uuid,
    from_email: &str,
    to_email: &str,
    cc: Option<&str>,
    bcc: Option<&str>,
    subject: &str,
) -> Result<(), String> {
    let mut db_conn = conn
        .get()
        .map_err(|e| format!("DB connection error: {}", e))?;

    let id = Uuid::new_v4();
    let now = Utc::now();

    diesel::sql_query(
        "INSERT INTO sent_email_tracking
           (id, tracking_id, bot_id, account_id, from_email, to_email, cc, bcc, subject, sent_at, read_count, is_read)
           VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 0, false)"
    )
    .bind::<diesel::sql_types::Uuid, _>(id)
    .bind::<diesel::sql_types::Uuid, _>(tracking_id)
    .bind::<diesel::sql_types::Uuid, _>(bot_id)
    .bind::<diesel::sql_types::Uuid, _>(account_id)
    .bind::<diesel::sql_types::Text, _>(from_email)
    .bind::<diesel::sql_types::Text, _>(to_email)
    .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(cc)
    .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(bcc)
    .bind::<diesel::sql_types::Text, _>(subject)
    .bind::<diesel::sql_types::Timestamptz, _>(now)
    .execute(&mut db_conn)
    .map_err(|e| format!("Failed to save tracking record: {}", e))?;

    debug!("Saved email tracking record: tracking_id={}", tracking_id);
    Ok(())
}

pub async fn serve_tracking_pixel(
    Path(tracking_id): Path<String>,
    State(state): State<Arc<AppState>>,
    Query(_query): Query<TrackingPixelQuery>,
    headers: axum::http::HeaderMap,
) -> impl IntoResponse {
    let client_ip = headers
        .get("x-forwarded-for")
        .and_then(|v| v.to_str().ok())
        .map(|s| s.split(',').next().unwrap_or(s).trim().to_string())
        .or_else(|| {
            headers
                .get("x-real-ip")
                .and_then(|v| v.to_str().ok())
                .map(|s| s.to_string())
        });

    let user_agent = headers
        .get("user-agent")
        .and_then(|v| v.to_str().ok())
        .map(|s| s.to_string());

    if let Ok(tracking_uuid) = Uuid::parse_str(&tracking_id) {
        let conn = state.conn.clone();
        let ip_clone = client_ip.clone();
        let ua_clone = user_agent.clone();

        let _ = tokio::task::spawn_blocking(move || {
            update_email_read_status(conn, tracking_uuid, ip_clone, ua_clone)
        })
        .await;

        info!(
            "Email read tracked: tracking_id={}, ip={:?}",
            tracking_id, client_ip
        );
    } else {
        warn!("Invalid tracking ID received: {}", tracking_id);
    }

    (
        StatusCode::OK,
        [
            ("content-type", "image/gif"),
            (
                "cache-control",
                "no-store, no-cache, must-revalidate, max-age=0",
            ),
            ("pragma", "no-cache"),
            ("expires", "0"),
        ],
        TRACKING_PIXEL.to_vec(),
    )
}

fn update_email_read_status(
    conn: crate::shared::utils::DbPool,
    tracking_id: Uuid,
    client_ip: Option<String>,
    user_agent: Option<String>,
) -> Result<(), String> {
    let mut db_conn = conn
        .get()
        .map_err(|e| format!("DB connection error: {}", e))?;
    let now = Utc::now();

    diesel::sql_query(
        r"UPDATE sent_email_tracking
           SET
               is_read = true,
               read_count = read_count + 1,
               read_at = COALESCE(read_at, $2),
               first_read_ip = COALESCE(first_read_ip, $3),
               last_read_ip = $3,
               user_agent = COALESCE(user_agent, $4),
               updated_at = $2
           WHERE tracking_id = $1",
    )
    .bind::<diesel::sql_types::Uuid, _>(tracking_id)
    .bind::<diesel::sql_types::Timestamptz, _>(now)
    .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(client_ip.as_deref())
    .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(user_agent.as_deref())
    .execute(&mut db_conn)
    .map_err(|e| format!("Failed to update tracking record: {}", e))?;

    debug!("Updated email read status: tracking_id={}", tracking_id);
    Ok(())
}

pub async fn get_tracking_status(
    Path(tracking_id): Path<String>,
    State(state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<TrackingStatusResponse>>, EmailError> {
    let tracking_uuid =
        Uuid::parse_str(&tracking_id).map_err(|_| EmailError("Invalid tracking ID".to_string()))?;

    let conn = state.conn.clone();
    let result = tokio::task::spawn_blocking(move || get_tracking_record(conn, tracking_uuid))
        .await
        .map_err(|e| EmailError(format!("Task join error: {}", e)))?
        .map_err(EmailError)?;

    Ok(Json(ApiResponse {
        success: true,
        data: Some(result),
        message: None,
    }))
}

fn get_tracking_record(
    conn: crate::shared::utils::DbPool,
    tracking_id: Uuid,
) -> Result<TrackingStatusResponse, String> {
    let mut db_conn = conn
        .get()
        .map_err(|e| format!("DB connection error: {}", e))?;

    #[derive(QueryableByName)]
    struct TrackingRow {
        #[diesel(sql_type = diesel::sql_types::Uuid)]
        tracking_id: Uuid,
        #[diesel(sql_type = diesel::sql_types::Text)]
        to_email: String,
        #[diesel(sql_type = diesel::sql_types::Text)]
        subject: String,
        #[diesel(sql_type = diesel::sql_types::Timestamptz)]
        sent_at: DateTime<Utc>,
        #[diesel(sql_type = diesel::sql_types::Bool)]
        is_read: bool,
        #[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Timestamptz>)]
        read_at: Option<DateTime<Utc>>,
        #[diesel(sql_type = diesel::sql_types::Integer)]
        read_count: i32,
    }

    let row: TrackingRow = diesel::sql_query(
        r"SELECT tracking_id, to_email, subject, sent_at, is_read, read_at, read_count
           FROM sent_email_tracking WHERE tracking_id = $1",
    )
    .bind::<diesel::sql_types::Uuid, _>(tracking_id)
    .get_result(&mut db_conn)
    .map_err(|e| format!("Tracking record not found: {}", e))?;

    Ok(TrackingStatusResponse {
        tracking_id: row.tracking_id.to_string(),
        to_email: row.to_email,
        subject: row.subject,
        sent_at: row.sent_at.to_rfc3339(),
        is_read: row.is_read,
        read_at: row.read_at.map(|dt| dt.to_rfc3339()),
        read_count: row.read_count,
    })
}

pub async fn list_sent_emails_tracking(
    State(state): State<Arc<AppState>>,
    Query(query): Query<ListTrackingQuery>,
) -> Result<Json<ApiResponse<Vec<TrackingStatusResponse>>>, EmailError> {
    let conn = state.conn.clone();
    let result = tokio::task::spawn_blocking(move || list_tracking_records(conn, query))
        .await
        .map_err(|e| EmailError(format!("Task join error: {}", e)))?
        .map_err(EmailError)?;

    Ok(Json(ApiResponse {
        success: true,
        data: Some(result),
        message: None,
    }))
}

fn list_tracking_records(
    conn: crate::shared::utils::DbPool,
    query: ListTrackingQuery,
) -> Result<Vec<TrackingStatusResponse>, String> {
    let mut db_conn = conn
        .get()
        .map_err(|e| format!("DB connection error: {}", e))?;

    let limit = query.limit.unwrap_or(50);
    let offset = query.offset.unwrap_or(0);

    #[derive(QueryableByName)]
    struct TrackingRow {
        #[diesel(sql_type = diesel::sql_types::Uuid)]
        tracking_id: Uuid,
        #[diesel(sql_type = diesel::sql_types::Text)]
        to_email: String,
        #[diesel(sql_type = diesel::sql_types::Text)]
        subject: String,
        #[diesel(sql_type = diesel::sql_types::Timestamptz)]
        sent_at: DateTime<Utc>,
        #[diesel(sql_type = diesel::sql_types::Bool)]
        is_read: bool,
        #[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Timestamptz>)]
        read_at: Option<DateTime<Utc>>,
        #[diesel(sql_type = diesel::sql_types::Integer)]
        read_count: i32,
    }

    let base_query = match query.filter.as_deref() {
        Some("read") => {
            "SELECT tracking_id, to_email, subject, sent_at, is_read, read_at, read_count
               FROM sent_email_tracking WHERE account_id = $1 AND is_read = true
               ORDER BY sent_at DESC LIMIT $2 OFFSET $3"
        }
        Some("unread") => {
            "SELECT tracking_id, to_email, subject, sent_at, is_read, read_at, read_count
               FROM sent_email_tracking WHERE account_id = $1 AND is_read = false
               ORDER BY sent_at DESC LIMIT $2 OFFSET $3"
        }
        _ => {
            "SELECT tracking_id, to_email, subject, sent_at, is_read, read_at, read_count
               FROM sent_email_tracking WHERE account_id = $1
               ORDER BY sent_at DESC LIMIT $2 OFFSET $3"
        }
    };

    let rows: Vec<TrackingRow> = diesel::sql_query(base_query)
        .bind::<diesel::sql_types::BigInt, _>(limit)
        .bind::<diesel::sql_types::BigInt, _>(offset)
        .load(&mut db_conn)
        .map_err(|e| format!("Query failed: {}", e))?;

    Ok(rows
        .into_iter()
        .map(|row| TrackingStatusResponse {
            tracking_id: row.tracking_id.to_string(),
            to_email: row.to_email,
            subject: row.subject,
            sent_at: row.sent_at.to_rfc3339(),
            is_read: row.is_read,
            read_at: row.read_at.map(|dt| dt.to_rfc3339()),
            read_count: row.read_count,
        })
        .collect())
}

pub async fn get_tracking_stats(
    State(state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<TrackingStatsResponse>>, EmailError> {
    let conn = state.conn.clone();
    let result = tokio::task::spawn_blocking(move || calculate_tracking_stats(conn))
        .await
        .map_err(|e| EmailError(format!("Task join error: {}", e)))?
        .map_err(EmailError)?;

    Ok(Json(ApiResponse {
        success: true,
        data: Some(result),
        message: None,
    }))
}

fn calculate_tracking_stats(
    conn: crate::shared::utils::DbPool,
) -> Result<TrackingStatsResponse, String> {
    let mut db_conn = conn
        .get()
        .map_err(|e| format!("DB connection error: {}", e))?;

    #[derive(QueryableByName)]
    struct StatsRow {
        #[diesel(sql_type = diesel::sql_types::BigInt)]
        total_sent: i64,
        #[diesel(sql_type = diesel::sql_types::BigInt)]
        total_read: i64,
        #[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Double>)]
        avg_time_hours: Option<f64>,
    }

    let stats: StatsRow = diesel::sql_query(
        r"SELECT
               COUNT(*) as total_sent,
               COUNT(*) FILTER (WHERE is_read = true) as total_read,
               AVG(EXTRACT(EPOCH FROM (read_at - sent_at)) / 3600) FILTER (WHERE is_read = true) as avg_time_hours
           FROM sent_email_tracking",
    )
    .get_result(&mut db_conn)
    .map_err(|e| format!("Stats query failed: {}", e))?;

    let read_rate = if stats.total_sent > 0 {
        (stats.total_read as f64 / stats.total_sent as f64) * 100.0
    } else {
        0.0
    };

    Ok(TrackingStatsResponse {
        total_sent: stats.total_sent,
        total_read: stats.total_read,
        read_rate,
        avg_time_to_read_hours: stats.avg_time_hours,
    })
}

pub fn get_emails(Path(campaign_id): Path<String>, State(_state): State<Arc<AppState>>) -> String {
    info!("Get emails requested for campaign: {campaign_id}");
    "No emails tracked".to_string()
}

pub struct EmailService {
    state: Arc<AppState>,
}

impl EmailService {
    pub fn new(state: Arc<AppState>) -> Self {
        Self { state }
    }

    pub fn send_email(
        &self,
        to: &str,
        subject: &str,
        body: &str,
        cc: Option<Vec<String>>,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let config = self
            .state
            .config
            .as_ref()
            .ok_or("Email configuration not available")?;

        let from_addr = config
            .email
            .from
            .parse()
            .map_err(|e| format!("Invalid from address: {}", e))?;

        let mut email_builder = Message::builder()
            .from(from_addr)
            .to(to.parse()?)
            .subject(subject);

        if let Some(cc_list) = cc {
            for cc_addr in cc_list {
                email_builder = email_builder.cc(cc_addr.parse()?);
            }
        }

        let email = email_builder.body(body.to_string())?;

        let creds = Credentials::new(config.email.username.clone(), config.email.password.clone());

        let mailer = SmtpTransport::relay(&config.email.smtp_server)?
            .credentials(creds)
            .build();

        mailer.send(&email)?;
        Ok(())
    }

    pub fn send_email_with_attachment(
        &self,
        to: &str,
        subject: &str,
        body: &str,
        _attachment: Vec<u8>,
        _filename: &str,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.send_email(to, subject, body, None)
    }
}

pub async fn fetch_latest_sent_to(config: &EmailConfig, to: &str) -> Result<String, String> {
    let client = imap::ClientBuilder::new(&config.server, config.port)
        .connect()
        .map_err(|e| format!("Connection error: {}", e))?;

    let mut session = client
        .login(&config.username, &config.password)
        .map_err(|e| format!("Login failed: {:?}", e))?;

    session
        .select("INBOX")
        .map_err(|e| format!("Select INBOX failed: {}", e))?;

    let search_query = format!("TO \"{}\"", to);
    let message_ids = session
        .search(&search_query)
        .map_err(|e| format!("Search failed: {}", e))?;

    if let Some(&last_id) = message_ids.iter().max() {
        let messages = session
            .fetch(last_id.to_string(), "BODY[TEXT]")
            .map_err(|e| format!("Fetch failed: {}", e))?;

        if let Some(message) = messages.iter().next() {
            if let Some(body) = message.text() {
                return Ok(String::from_utf8_lossy(body).to_string());
            }
        }
    }

    session.logout().ok();
    Ok(String::new())
}

pub async fn save_email_draft(
    config: &EmailConfig,
    draft: &SaveDraftRequest,
) -> Result<(), String> {
    use chrono::Utc;

    let client = imap::ClientBuilder::new(&config.server, config.port)
        .connect()
        .map_err(|e| format!("Connection error: {}", e))?;

    let mut session = client
        .login(&config.username, &config.password)
        .map_err(|e| format!("Login failed: {:?}", e))?;

    let date = Utc::now().to_rfc2822();
    let message_id = format!("<{}.{}@botserver>", Uuid::new_v4(), config.server);
    let cc_header = if let Some(cc) = &draft.cc {
        format!("Cc: {}\r\n", cc)
    } else {
        String::new()
    };

    let email_content = format!(
        "Date: {}\r\n\
         From: {}\r\n\
         To: {}\r\n\
         {}\
         Subject: {}\r\n\
         Message-ID: {}\r\n\
         Content-Type: text/html; charset=UTF-8\r\n\
         \r\n\
         {}",
        date, config.from, draft.to, cc_header, draft.subject, message_id, draft.body
    );

    let folder = session
        .list(None, Some("Drafts"))
        .map_err(|e| format!("List folders failed: {}", e))?
        .iter()
        .find(|name| name.name().to_lowercase().contains("draft"))
        .map(|n| n.name().to_string())
        .unwrap_or_else(|| "INBOX".to_string());

    session
        .append(&folder, email_content.as_bytes())
        .finish()
        .map_err(|e| format!("Append draft failed: {}", e))?;

    session.logout().ok();
    info!("Draft saved to: {}, subject: {}", draft.to, draft.subject);
    Ok(())
}

fn fetch_emails_from_folder(
    config: &EmailConfig,
    folder: &str,
) -> Result<Vec<EmailSummary>, String> {
    let client = imap::ClientBuilder::new(&config.server, config.port)
        .connect()
        .map_err(|e| format!("Connection error: {}", e))?;

    let mut session = client
        .login(&config.username, &config.password)
        .map_err(|e| format!("Login failed: {:?}", e))?;

    let folder_name = match folder {
        "sent" => "Sent",
        "drafts" => "Drafts",
        "trash" => "Trash",
        _ => "INBOX",
    };

    session
        .select(folder_name)
        .map_err(|e| format!("Select folder failed: {}", e))?;

    let messages = session
        .fetch("1:20", "(FLAGS RFC822.HEADER)")
        .map_err(|e| format!("Fetch failed: {}", e))?;

    let mut emails = Vec::new();
    for message in messages.iter() {
        if let Some(header) = message.header() {
            let parsed = parse_mail(header).ok();
            if let Some(mail) = parsed {
                let subject = mail.headers.get_first_value("Subject").unwrap_or_default();
                let from = mail.headers.get_first_value("From").unwrap_or_default();
                let date = mail.headers.get_first_value("Date").unwrap_or_default();
                let flags = message.flags();
                let unread = !flags.iter().any(|f| matches!(f, imap::types::Flag::Seen));

                let preview = subject.chars().take(100).collect();
                emails.push(EmailSummary {
                    id: message.message.to_string(),
                    from,
                    subject,
                    date,
                    preview,
                    unread,
                });
            }
        }
    }

    session.logout().ok();
    Ok(emails)
}

fn get_folder_counts(
    config: &EmailConfig,
) -> Result<std::collections::HashMap<String, usize>, String> {
    use std::collections::HashMap;

    let client = imap::ClientBuilder::new(&config.server, config.port)
        .connect()
        .map_err(|e| format!("Connection error: {}", e))?;

    let mut session = client
        .login(&config.username, &config.password)
        .map_err(|e| format!("Login failed: {:?}", e))?;

    let mut counts = HashMap::new();

    for folder in ["INBOX", "Sent", "Drafts", "Trash"] {
        if let Ok(mailbox) = session.examine(folder) {
            counts.insert((*folder).to_string(), mailbox.exists as usize);
        }
    }

    session.logout().ok();
    Ok(counts)
}

fn fetch_email_by_id(config: &EmailConfig, id: &str) -> Result<EmailContent, String> {
    let client = imap::ClientBuilder::new(&config.server, config.port)
        .connect()
        .map_err(|e| format!("Connection error: {}", e))?;

    let mut session = client
        .login(&config.username, &config.password)
        .map_err(|e| format!("Login failed: {:?}", e))?;

    session
        .select("INBOX")
        .map_err(|e| format!("Select failed: {}", e))?;

    let messages = session
        .fetch(id, "RFC822")
        .map_err(|e| format!("Fetch failed: {}", e))?;

    if let Some(message) = messages.iter().next() {
        if let Some(body) = message.body() {
            let parsed = parse_mail(body).map_err(|e| format!("Parse failed: {}", e))?;

            let subject = parsed
                .headers
                .get_first_value("Subject")
                .unwrap_or_default();
            let from = parsed.headers.get_first_value("From").unwrap_or_default();
            let to = parsed.headers.get_first_value("To").unwrap_or_default();
            let date = parsed.headers.get_first_value("Date").unwrap_or_default();

            let body_text = parsed
                .subparts
                .iter()
                .find_map(|p| p.get_body().ok())
                .or_else(|| parsed.get_body().ok())
                .unwrap_or_default();

            session.logout().ok();

            return Ok(EmailContent {
                subject,
                from,
                to,
                date,
                body: body_text,
            });
        }
    }

    session.logout().ok();
    Err("Email not found".to_string())
}

fn move_email_to_trash(config: &EmailConfig, id: &str) -> Result<(), String> {
    let client = imap::ClientBuilder::new(&config.server, config.port)
        .connect()
        .map_err(|e| format!("Connection error: {}", e))?;

    let mut session = client
        .login(&config.username, &config.password)
        .map_err(|e| format!("Login failed: {:?}", e))?;

    session
        .select("INBOX")
        .map_err(|e| format!("Select failed: {}", e))?;

    session
        .store(id, "+FLAGS (\\Deleted)")
        .map_err(|e| format!("Store failed: {}", e))?;

    session
        .expunge()
        .map_err(|e| format!("Expunge failed: {}", e))?;

    session.logout().ok();
    Ok(())
}

#[derive(Debug)]
struct EmailSummary {
    id: String,
    from: String,
    subject: String,
    date: String,
    preview: String,
    unread: bool,
}

#[derive(Debug)]
struct EmailContent {
    subject: String,
    from: String,
    to: String,
    date: String,
    body: String,
}

pub async fn list_emails_htmx(
    State(state): State<Arc<AppState>>,
    Query(params): Query<std::collections::HashMap<String, String>>,
) -> Result<impl IntoResponse, EmailError> {
    let folder = params
        .get("folder")
        .cloned()
        .unwrap_or_else(|| "inbox".to_string());

    let user_id = extract_user_from_session(&state)
        .map_err(|_| EmailError("Authentication required".to_string()))?;

    let conn = state.conn.clone();
    let account = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {}", e))?;

        diesel::sql_query("SELECT * FROM email_accounts WHERE user_id = $1 LIMIT 1")
            .bind::<diesel::sql_types::Uuid, _>(user_id)
            .get_result::<EmailAccountRow>(&mut db_conn)
            .optional()
            .map_err(|e| format!("Failed to get email account: {}", e))
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    let Some(account) = account else {
        return Ok(axum::response::Html(
            r#"<div class="empty-state">
                <h3>No email account configured</h3>
                <p>Please add an email account first</p>
            </div>"#
                .to_string(),
        ));
    };

    let config = EmailConfig {
        username: account.username.clone(),
        password: account.password.clone(),
        server: account.imap_server.clone(),
        port: account.imap_port as u16,
        from: account.email.clone(),
        smtp_server: account.smtp_server.clone(),
        smtp_port: account.smtp_port as u16,
    };

    let emails = fetch_emails_from_folder(&config, &folder).unwrap_or_default();

    let mut html = String::new();
    use std::fmt::Write;
    for email in &emails {
        let unread_class = if email.unread { "unread" } else { "" };
        let _ = write!(
            html,
            r##"<div class="mail-item {}"
                 hx-get="/api/email/{}"
                 hx-target="#mail-content"
                 hx-swap="innerHTML">
                <div class="mail-header">
                    <span>{}</span>
                    <span class="text-sm text-gray">{}</span>
                </div>
                <div class="mail-subject">{}</div>
                <div class="mail-preview">{}</div>
            </div>"##,
            unread_class, email.id, email.from, email.date, email.subject, email.preview
        );
    }

    if html.is_empty() {
        html = format!(
            r#"<div class="empty-state">
                <h3>No emails in {}</h3>
                <p>This folder is empty</p>
            </div>"#,
            folder
        );
    }

    Ok(axum::response::Html(html))
}

pub async fn list_folders_htmx(
    State(state): State<Arc<AppState>>,
) -> Result<impl IntoResponse, EmailError> {
    let user_id = extract_user_from_session(&state)
        .map_err(|_| EmailError("Authentication required".to_string()))?;

    let conn = state.conn.clone();
    let account = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {}", e))?;

        diesel::sql_query("SELECT * FROM email_accounts WHERE user_id = $1 LIMIT 1")
            .bind::<diesel::sql_types::Uuid, _>(user_id)
            .get_result::<EmailAccountRow>(&mut db_conn)
            .optional()
            .map_err(|e| format!("Failed to get email account: {}", e))
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    if account.is_none() {
        return Ok(axum::response::Html(
            r#"<div class="nav-item">No account configured</div>"#.to_string(),
        ));
    }

    let Some(account) = account else {
        return Ok(Html(
            r#"<div class="nav-item">Account not found</div>"#.to_string(),
        ));
    };

    let config = EmailConfig {
        username: account.username.clone(),
        password: account.password.clone(),
        server: account.imap_server.clone(),
        port: account.imap_port as u16,
        from: account.email.clone(),
        smtp_server: account.smtp_server.clone(),
        smtp_port: account.smtp_port as u16,
    };

    let folder_counts = get_folder_counts(&config).unwrap_or_default();

    let mut html = String::new();
    for (folder_name, icon, count) in &[
        ("inbox", "", folder_counts.get("INBOX").unwrap_or(&0)),
        ("sent", "", folder_counts.get("Sent").unwrap_or(&0)),
        ("drafts", "", folder_counts.get("Drafts").unwrap_or(&0)),
        ("trash", "", folder_counts.get("Trash").unwrap_or(&0)),
    ] {
        let active = if *folder_name == "inbox" {
            "active"
        } else {
            ""
        };
        let count_badge = if **count > 0 {
            format!(
                r#"<span style="margin-left: auto; font-size: 0.875rem; color: #64748b;">{}</span>"#,
                count
            )
        } else {
            String::new()
        };

        use std::fmt::Write;
        let _ = write!(
            html,
            r##"<div class="nav-item {}"
                 hx-get="/api/email/list?folder={}"
                 hx-target="#mail-list"
                 hx-swap="innerHTML">
                <span>{}</span> {}
                {}
            </div>"##,
            active,
            folder_name,
            icon,
            folder_name
                .chars()
                .next()
                .unwrap_or_default()
                .to_uppercase()
                .collect::<String>()
                + &folder_name[1..],
            count_badge
        );
    }

    Ok(axum::response::Html(html))
}

pub async fn compose_email_htmx(
    State(_state): State<Arc<AppState>>,
) -> Result<impl IntoResponse, EmailError> {
    let html = r##"
        <div class="mail-content-view">
            <h2>Compose New Email</h2>
            <form class="compose-form"
                  hx-post="/api/email/send"
                  hx-target="#mail-content"
                  hx-swap="innerHTML">
                <div class="form-group">
                    <label>To:</label>
                    <input type="email" name="to" required>
                </div>
                <div class="form-group">
                    <label>Subject:</label>
                    <input type="text" name="subject" required>
                </div>
                <div class="form-group">
                    <label>Message:</label>
                    <textarea name="body" rows="10" required></textarea>
                </div>
                <div class="compose-actions">
                    <button type="submit" class="btn-primary">Send</button>
                    <button type="button" class="btn-secondary"
                            hx-post="/api/email/draft"
                            hx-include="closest form">Save Draft</button>
                </div>
            </form>
        </div>
    "##;

    Ok(axum::response::Html(html))
}

pub async fn get_email_content_htmx(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<impl IntoResponse, EmailError> {
    let user_id = extract_user_from_session(&state)
        .map_err(|_| EmailError("Authentication required".to_string()))?;

    let conn = state.conn.clone();
    let account = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {}", e))?;

        diesel::sql_query("SELECT * FROM email_accounts WHERE user_id = $1 LIMIT 1")
            .bind::<diesel::sql_types::Uuid, _>(user_id)
            .get_result::<EmailAccountRow>(&mut db_conn)
            .optional()
            .map_err(|e| format!("Failed to get email account: {}", e))
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    let Some(account) = account else {
        return Ok(axum::response::Html(
            r#"<div class="mail-content-view">
                <p>No email account configured</p>
            </div>"#
                .to_string(),
        ));
    };

    let config = EmailConfig {
        username: account.username.clone(),
        password: account.password.clone(),
        server: account.imap_server.clone(),
        port: account.imap_port as u16,
        from: account.email.clone(),
        smtp_server: account.smtp_server.clone(),
        smtp_port: account.smtp_port as u16,
    };

    let email_content = fetch_email_by_id(&config, &id)
        .map_err(|e| EmailError(format!("Failed to fetch email: {}", e)))?;

    let html = format!(
        r##"
        <div class="mail-content-view">
            <div class="mail-actions">
                <button hx-get="/api/email/compose?reply_to={}"
                        hx-target="#mail-content"
                        hx-swap="innerHTML">Reply</button>
                <button hx-get="/api/email/compose?forward={}"
                        hx-target="#mail-content"
                        hx-swap="innerHTML">Forward</button>
                <button hx-delete="/api/email/{}"
                        hx-target="#mail-list"
                        hx-swap="innerHTML"
                        hx-confirm="Delete this email?">Delete</button>
            </div>
            <h2>{}</h2>
            <div style="display: flex; align-items: center; gap: 1rem; margin: 1rem 0;">
                <div>
                    <div style="font-weight: 600;">{}</div>
                    <div class="text-sm text-gray">to: {}</div>
                </div>
                <div style="margin-left: auto;" class="text-sm text-gray">{}</div>
            </div>
            <div class="mail-body">
                {}
            </div>
        </div>
        "##,
        id,
        id,
        id,
        email_content.subject,
        email_content.from,
        email_content.to,
        email_content.date,
        email_content.body
    );

    Ok(axum::response::Html(html))
}

pub async fn delete_email_htmx(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<impl IntoResponse, EmailError> {
    let user_id = extract_user_from_session(&state)
        .map_err(|_| EmailError("Authentication required".to_string()))?;

    let conn = state.conn.clone();
    let account = tokio::task::spawn_blocking(move || {
        let mut db_conn = conn
            .get()
            .map_err(|e| format!("DB connection error: {}", e))?;

        diesel::sql_query("SELECT * FROM email_accounts WHERE user_id = $1 LIMIT 1")
            .bind::<diesel::sql_types::Uuid, _>(user_id)
            .get_result::<EmailAccountRow>(&mut db_conn)
            .optional()
            .map_err(|e| format!("Failed to get email account: {}", e))
    })
    .await
    .map_err(|e| EmailError(format!("Task join error: {e}")))?
    .map_err(EmailError)?;

    if let Some(account) = account {
        let config = EmailConfig {
            username: account.username.clone(),
            password: account.password.clone(),
            server: account.imap_server.clone(),
            port: account.imap_port as u16,
            from: account.email.clone(),
            smtp_server: account.smtp_server.clone(),
            smtp_port: account.smtp_port as u16,
        };

        move_email_to_trash(&config, &id)
            .map_err(|e| EmailError(format!("Failed to delete email: {}", e)))?;
    }

    info!("Email {} moved to trash", id);

    list_emails_htmx(State(state), Query(std::collections::HashMap::new())).await
}

pub async fn get_latest_email(
    State(_state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<EmailData>>, EmailError> {
    Ok(Json(ApiResponse {
        success: true,
        data: Some(EmailData {
            id: Uuid::new_v4().to_string(),
            from: "sender@example.com".to_string(),
            to: "recipient@example.com".to_string(),
            subject: "Latest Email".to_string(),
            body: "This is the latest email content.".to_string(),
            date: chrono::Utc::now().to_rfc3339(),
            unread: true,
        }),
        message: Some("Latest email fetched".to_string()),
    }))
}

pub async fn get_email(
    State(_state): State<Arc<AppState>>,
    Path(campaign_id): Path<String>,
) -> Result<Json<ApiResponse<EmailData>>, EmailError> {
    Ok(Json(ApiResponse {
        success: true,
        data: Some(EmailData {
            id: campaign_id,
            from: "sender@example.com".to_string(),
            to: "recipient@example.com".to_string(),
            subject: "Email Subject".to_string(),
            body: "Email content here.".to_string(),
            date: chrono::Utc::now().to_rfc3339(),
            unread: false,
        }),
        message: Some("Email fetched".to_string()),
    }))
}

pub async fn track_click(
    State(_state): State<Arc<AppState>>,
    Path((campaign_id, email)): Path<(String, String)>,
) -> Result<Json<ApiResponse<()>>, EmailError> {
    info!(
        "Tracking click for campaign {} email {}",
        campaign_id, email
    );

    Ok(Json(ApiResponse {
        success: true,
        data: Some(()),
        message: Some("Click tracked".to_string()),
    }))
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EmailData {
    pub id: String,
    pub from: String,
    pub to: String,
    pub subject: String,
    pub body: String,
    pub date: String,
    pub unread: bool,
}

#[derive(Debug, QueryableByName)]
struct EmailAccountRow {
    #[diesel(sql_type = diesel::sql_types::Uuid)]
    pub _id: Uuid,
    #[diesel(sql_type = diesel::sql_types::Uuid)]
    pub _user_id: Uuid,
    #[diesel(sql_type = diesel::sql_types::Text)]
    pub email: String,
    #[diesel(sql_type = diesel::sql_types::Text)]
    pub username: String,
    #[diesel(sql_type = diesel::sql_types::Text)]
    pub password: String,
    #[diesel(sql_type = diesel::sql_types::Text)]
    pub imap_server: String,
    #[diesel(sql_type = diesel::sql_types::Integer)]
    pub imap_port: i32,
    #[diesel(sql_type = diesel::sql_types::Text)]
    pub smtp_server: String,
    #[diesel(sql_type = diesel::sql_types::Integer)]
    pub smtp_port: i32,
}

pub async fn list_labels_htmx(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
    axum::response::Html(
        r#"
        <div class="label-item" style="--label-color: #ef4444;">
            <span class="label-dot" style="background: #ef4444;"></span>
            <span>Important</span>
        </div>
        <div class="label-item" style="--label-color: #3b82f6;">
            <span class="label-dot" style="background: #3b82f6;"></span>
            <span>Work</span>
        </div>
        <div class="label-item" style="--label-color: #22c55e;">
            <span class="label-dot" style="background: #22c55e;"></span>
            <span>Personal</span>
        </div>
        <div class="label-item" style="--label-color: #f59e0b;">
            <span class="label-dot" style="background: #f59e0b;"></span>
            <span>Finance</span>
        </div>
    "#
        .to_string(),
    )
}

pub async fn list_templates_htmx(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
    axum::response::Html(
        r#"
        <div class="template-item" onclick="useTemplate('welcome')">
            <h4>Welcome Email</h4>
            <p>Standard welcome message for new contacts</p>
        </div>
        <div class="template-item" onclick="useTemplate('followup')">
            <h4>Follow Up</h4>
            <p>General follow-up template</p>
        </div>
        <div class="template-item" onclick="useTemplate('meeting')">
            <h4>Meeting Request</h4>
            <p>Request a meeting with scheduling options</p>
        </div>
        <p class="text-sm text-gray" style="margin-top: 1rem; text-align: center;">
            Click a template to use it
        </p>
    "#
        .to_string(),
    )
}

pub async fn list_signatures_htmx(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
    axum::response::Html(
        r#"
        <div class="signature-item" onclick="useSignature('default')">
            <h4>Default Signature</h4>
            <p class="text-sm text-gray">Best regards,<br>Your Name</p>
        </div>
        <div class="signature-item" onclick="useSignature('formal')">
            <h4>Formal Signature</h4>
            <p class="text-sm text-gray">Sincerely,<br>Your Name<br>Title | Company</p>
        </div>
        <p class="text-sm text-gray" style="margin-top: 1rem; text-align: center;">
            Click a signature to insert it
        </p>
    "#
        .to_string(),
    )
}

pub async fn list_rules_htmx(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
    axum::response::Html(
        r#"
        <div class="rule-item">
            <div class="rule-header">
                <span class="rule-name">Auto-archive newsletters</span>
                <label class="toggle-label">
                    <input type="checkbox" checked>
                    <span class="toggle-switch"></span>
                </label>
            </div>
            <p class="text-sm text-gray">From: *@newsletter.* → Archive</p>
        </div>
        <div class="rule-item">
            <div class="rule-header">
                <span class="rule-name">Label work emails</span>
                <label class="toggle-label">
                    <input type="checkbox" checked>
                    <span class="toggle-switch"></span>
                </label>
            </div>
            <p class="text-sm text-gray">From: *@company.com → Label: Work</p>
        </div>
        <button class="btn-secondary" style="width: 100%; margin-top: 1rem;">
            + Add New Rule
        </button>
    "#
        .to_string(),
    )
}

pub async fn search_emails_htmx(
    State(state): State<Arc<AppState>>,
    Query(params): Query<std::collections::HashMap<String, String>>,
) -> impl IntoResponse {
    let query = params.get("q").map(|s| s.as_str()).unwrap_or("");

    if query.is_empty() {
        return axum::response::Html(
            r#"
            <div class="empty-state">
                <p>Enter a search term to find emails</p>
            </div>
        "#
            .to_string(),
        );
    }

    let search_term = format!("%{query_lower}%", query_lower = query.to_lowercase());

    let Ok(mut conn) = state.conn.get() else {
        return axum::response::Html(
            r#"
                <div class="empty-state error">
                    <p>Database connection error</p>
                </div>
            "#
            .to_string(),
        );
    };

    let search_query = "SELECT id, subject, from_address, to_addresses, body_text, received_at
         FROM emails
         WHERE LOWER(subject) LIKE $1
            OR LOWER(from_address) LIKE $1
            OR LOWER(body_text) LIKE $1
         ORDER BY received_at DESC
         LIMIT 50";

    let results: Vec<EmailSearchRow> = match diesel::sql_query(search_query)
        .bind::<diesel::sql_types::Text, _>(&search_term)
        .load::<EmailSearchRow>(&mut conn)
    {
        Ok(r) => r,
        Err(e) => {
            warn!("Email search query failed: {}", e);
            Vec::new()
        }
    };

    if results.is_empty() {
        return axum::response::Html(format!(
            r#"
            <div class="empty-state">
                <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                    <circle cx="11" cy="11" r="8"></circle>
                    <path d="m21 21-4.35-4.35"></path>
                </svg>
                <h3>No results for "{}"</h3>
                <p>Try different keywords or check your spelling.</p>
            </div>
        "#,
            query
        ));
    }

    let mut html = String::from(r#"<div class="search-results">"#);
    use std::fmt::Write;
    let _ = write!(
        html,
        r#"<div class="result-stats">Found {} results for "{}"</div>"#,
        results.len(),
        query
    );

    for row in results {
        let preview = row
            .body_text
            .as_deref()
            .unwrap_or("")
            .chars()
            .take(100)
            .collect::<String>();
        let formatted_date = row.received_at.format("%b %d, %Y").to_string();

        let _ = write!(
            html,
            r##"
            <div class="email-item" hx-get="/ui/mail/view/{}" hx-target="#email-content" hx-swap="innerHTML">
                <div class="email-sender">{}</div>
                <div class="email-subject">{}</div>
                <div class="email-preview">{}</div>
                <div class="email-date">{}</div>
            </div>
        "##,
            row.id, row.from_address, row.subject, preview, formatted_date
        );
    }

    html.push_str("</div>");
    axum::response::Html(html)
}

pub async fn save_auto_responder(
    State(_state): State<Arc<AppState>>,
    axum::Form(form): axum::Form<std::collections::HashMap<String, String>>,
) -> impl IntoResponse {
    info!("Saving auto-responder settings: {:?}", form);

    axum::response::Html(
        r#"
        <div class="notification success">
            Auto-responder settings saved successfully!
        </div>
    "#
        .to_string(),
    )
}
