use crate::node_registry::{NodeInfo, NodeRegistry};
use crate::task_queue::TaskQueue;
use anyhow::Result;
use async_trait::async_trait;
use chrono::Utc;
use enact_core::kernel::NodeId;
use enact_core::signal::SignalBus;
use std::sync::Arc;
use std::time::Duration;

#[async_trait]
pub trait TaskExecutor: Send + Sync {
    async fn execute(&self, task: crate::task_queue::DispatchTask) -> Result<serde_json::Value>;
}

pub struct Worker {
    pub node_id: NodeId,
    pub endpoint: String,
    pub capabilities: Vec<String>,
    pub heartbeat_interval: Duration,
    pub node_registry: Arc<dyn NodeRegistry>,
    pub task_queue: Arc<dyn TaskQueue>,
    pub signal_bus: Option<Arc<dyn SignalBus>>,
    pub executor: Arc<dyn TaskExecutor>,
}

impl Worker {
    pub async fn run(&self) -> Result<()> {
        self.node_registry
            .register(NodeInfo {
                node_id: self.node_id.clone(),
                endpoint: self.endpoint.clone(),
                capabilities: self.capabilities.clone(),
                load: 0.0,
                last_heartbeat: Utc::now(),
            })
            .await?;

        let mut ticker = tokio::time::interval(self.heartbeat_interval);

        loop {
            ticker.tick().await;
            let _ = self.node_registry.heartbeat(&self.node_id).await;

            if let Some(task) = self.task_queue.claim_next(&self.node_id).await? {
                let task_id = task.task_id.clone();
                match self.executor.execute(task).await {
                    Ok(result) => {
                        if let Some(bus) = &self.signal_bus {
                            let payload = serde_json::to_vec(&serde_json::json!({
                                "task_id": task_id.to_string(),
                                "status": "completed",
                                "result": result,
                            }))?;
                            let _ = bus.emit("cluster.results", &payload).await;
                        }
                        self.task_queue.ack(&task_id).await?;
                    }
                    Err(e) => {
                        self.task_queue.fail(&task_id, &e.to_string()).await?;
                    }
                }
            }
        }
    }
}
