//! 过期数据清理：每 15 分钟执行一次。
//!
//! - 标记过期邮箱为 expired
//! - 物理删除过期邮件及其附件（按 expires_at）

use crate::repositories::{
    email_repository::EmailRepository, mailbox_repository::MailboxRepository,
};
use crate::services::email_service::EmailService;
use crate::AppState;
use std::time::Duration;

pub fn start(state: AppState) {
    tokio::spawn(async move {
        let mut ticker = tokio::time::interval(Duration::from_secs(15 * 60));
        ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
        ticker.tick().await; // 跳过启动时的首次立即触发
        loop {
            ticker.tick().await;
            if let Err(e) = run_once(&state).await {
                tracing::warn!(%e, "清理任务执行失败");
                state.runtime_health.cleanup_failed(e.to_string()).await;
            } else {
                state.runtime_health.cleanup_succeeded().await;
            }
        }
    });
}

async fn run_once(state: &AppState) -> anyhow::Result<()> {
    tracing::info!("清理任务开始");

    // 1. 过期邮箱标记 expired
    let mailbox_repo = MailboxRepository::new(state.pool.clone());
    let expired_mailboxes = mailbox_repo.list_expired_active().await?;
    for m in &expired_mailboxes {
        mailbox_repo.mark_expired(m.id).await?;
    }
    if !expired_mailboxes.is_empty() {
        tracing::info!("标记 {} 个过期邮箱", expired_mailboxes.len());
    }

    // 2. 物理删除过期邮件（含附件），分批
    let email_repo = EmailRepository::new(state.pool.clone());
    let email_service = EmailService::new(state.pool.clone(), state.config.smtp.clone());
    let mut total_deleted = 0usize;
    loop {
        let batch = email_repo.list_expired(500).await?;
        if batch.is_empty() {
            break;
        }
        let n = batch.len();
        for e in &batch {
            if let Err(error) = email_service.delete(e.mailbox_id, e.id).await {
                tracing::warn!(email_id = e.id, %error, "删除过期邮件失败");
            }
        }
        total_deleted += n;
    }
    if total_deleted > 0 {
        tracing::info!("清理 {} 封过期邮件", total_deleted);
    }

    tracing::info!("清理任务完成");
    Ok(())
}
