use deadpool_redis::cluster::Pool;
use serde::Serialize;
use std::sync::Arc;

#[derive(Debug, Clone, Serialize)]
pub struct TaskLog {
    pub task_id: String,
    pub repo_id: String,
    pub worker_id: String,
    pub level: String,
    pub message: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

pub struct LogStream {
    channel: String,
    worker_id: String,
    pool: Arc<Pool>,
}

impl Clone for LogStream {
    fn clone(&self) -> Self {
        Self {
            channel: self.channel.clone(),
            worker_id: self.worker_id.clone(),
            pool: self.pool.clone(),
        }
    }
}

impl LogStream {
    pub fn new(channel: String, worker_id: String, pool: Arc<Pool>) -> Self {
        Self {
            channel,
            worker_id,
            pool,
        }
    }

    async fn publish_log(&self, log: TaskLog) {
        let data = match serde_json::to_vec(&log) {
            Ok(d) => d,
            Err(e) => {
                eprintln!("failed to serialize log: {}", e);
                return;
            }
        };

        let redis = match self.pool.get().await {
            Ok(c) => c,
            Err(e) => {
                eprintln!("redis pool get failed: {}", e);
                return;
            }
        };

        let mut conn: deadpool_redis::cluster::Connection = redis;
        if let Err(e) = redis::cmd("PUBLISH")
            .arg(&self.channel)
            .arg(&data)
            .query_async::<()>(&mut conn)
            .await
        {
            eprintln!("Redis PUBLISH failed: {}", e);
        }
    }

    pub async fn info(&self, task_id: &str, repo_id: &str, message: &str) {
        self.publish_log(TaskLog {
            task_id: task_id.to_string(),
            repo_id: repo_id.to_string(),
            worker_id: self.worker_id.clone(),
            level: "info".to_string(),
            message: message.to_string(),
            timestamp: chrono::Utc::now(),
        })
        .await;
    }

    pub async fn error(&self, task_id: &str, repo_id: &str, message: &str) {
        self.publish_log(TaskLog {
            task_id: task_id.to_string(),
            repo_id: repo_id.to_string(),
            worker_id: self.worker_id.clone(),
            level: "error".to_string(),
            message: message.to_string(),
            timestamp: chrono::Utc::now(),
        })
        .await;
    }

    pub async fn warn(&self, task_id: &str, repo_id: &str, message: &str) {
        self.publish_log(TaskLog {
            task_id: task_id.to_string(),
            repo_id: repo_id.to_string(),
            worker_id: self.worker_id.clone(),
            level: "warn".to_string(),
            message: message.to_string(),
            timestamp: chrono::Utc::now(),
        })
        .await;
    }
}
