use migration::{Migrator, MigratorTrait};
use sea_orm::{ConnectOptions, Database, DbConn};
use std::env;
use std::path::PathBuf;
use tokio::sync::mpsc;
use tracing::info;

use crate::timer::{NotificationWorker, SharedTimeWheel, WorkerCommand};

#[derive(Clone)]
pub struct AppData {
    pub db: DbConn,
    pub jwt_secret: String,
    pub worker_tx: mpsc::Sender<WorkerCommand>,
    /// 服务器基础 URL，用于生成头像等资源的完整 URL
    /// 本地调试: http://localhost:4000
    /// 生产环境: https://notify.michaelandmeryl.xyz
    pub base_url: String,
    /// 上传文件的存储目录
    pub upload_dir: PathBuf,
}

impl AppData {
    pub async fn new() -> Result<Self, anyhow::Error> {
        let url = env::var("DATABASE_URL")
            .unwrap_or_else(|_| "postgres://postgres:postgres@localhost:5432/notify".to_string());
        let mut opt = ConnectOptions::new(url);
        opt.max_connections(10).sqlx_logging(false);
        let db = Database::connect(opt).await?;

        // 自动运行数据库迁移
        info!("Running database migrations...");
        Migrator::up(&db, None).await?;
        info!("Database migrations completed.");

        let jwt_secret = env::var("JWT_SECRET").unwrap_or_else(|_| "dev-secret".to_string());

        // 从环境变量读取 BASE_URL，默认为本地开发地址
        let base_url = env::var("BASE_URL").unwrap_or_else(|_| "http://localhost:4000".to_string());

        // 上传目录，默认为当前目录下的 uploads
        let upload_dir = env::var("UPLOAD_DIR")
            .map(PathBuf::from)
            .unwrap_or_else(|_| PathBuf::from("./uploads"));

        // 确保上传目录存在
        tokio::fs::create_dir_all(&upload_dir).await?;
        tokio::fs::create_dir_all(upload_dir.join("avatars")).await?;

        // 创建并启动时间轮 Worker
        let time_wheel = SharedTimeWheel::new();
        let worker = NotificationWorker::new(db.clone(), time_wheel);
        let worker_tx = worker.start().await;

        Ok(Self {
            db,
            jwt_secret,
            worker_tx,
            base_url,
            upload_dir,
        })
    }

    /// 发送命令给 Worker
    pub async fn send_worker_command(
        &self,
        cmd: WorkerCommand,
    ) -> Result<(), mpsc::error::SendError<WorkerCommand>> {
        self.worker_tx.send(cmd).await
    }
}
