use std::sync::Arc;

use provisioning_orchestrator::{
    monitor::{MonitoringEvent, MonitoringEventType},
    SharedState, TaskStatus,
};
use tracing::{error, info};

pub async fn execute_task(state: SharedState, mut task: provisioning_orchestrator::WorkflowTask) {
    let metrics_collector = state.monitoring_system.metrics_collector();

    metrics_collector.increment_task_counter();
    metrics_collector.record_storage_operation(true);

    if let Err(e) = state
        .task_storage
        .update_task_status(&task.id, TaskStatus::Running)
        .await
    {
        error!("Failed to update task status: {}", e);
        metrics_collector.record_storage_operation(false);
        return;
    }

    // Sync local task struct with the storage update so the failure path
    // does not overwrite started_at back to None via update_task().
    task.started_at = Some(chrono::Utc::now());
    task.status = TaskStatus::Running;

    #[cfg(feature = "nats")]
    state
        .publish_task_status(&task.id, "running", Some(0), None)
        .await;

    info!("Processing task: {} ({})", task.id, task.name);

    let task_start = std::time::Instant::now();
    let result = state
        .execute_nushell_command(&task.command, &task.args)
        .await;
    let task_duration = task_start.elapsed();

    match result {
        Ok(output) => {
            info!("Task {} completed successfully", task.id);

            task.output = Some(output);
            task.status = TaskStatus::Completed;
            task.completed_at = Some(chrono::Utc::now());

            #[cfg(feature = "nats")]
            state
                .publish_task_status(&task.id, "completed", Some(100), None)
                .await;

            #[cfg(feature = "nats")]
            if task.tags.get("type").map(|s| s.as_str()) == Some("component") {
                let event = provisioning_orchestrator::ComponentLifecycleEvent {
                    task_id: task.id.clone(),
                    component: task.tags.get("component").cloned().unwrap_or_default(),
                    server:    task.tags.get("server").cloned().unwrap_or_default(),
                    workspace: task.tags.get("workspace").cloned().unwrap_or_default(),
                    operation: task.tags.get("operation").cloned().unwrap_or_default(),
                    status:    "completed".to_string(),
                };
                state.publish_component_lifecycle(&event).await;
            }

            // Record metrics
            metrics_collector.record_task_completion(task_duration.as_millis() as u64);

            // Publish monitoring event
            let event = MonitoringEvent {
                event_type: MonitoringEventType::TaskStatusChanged,
                timestamp: chrono::Utc::now(),
                data: serde_json::to_value(&task).unwrap_or_default(),
                metadata: {
                    let mut meta = std::collections::HashMap::new();
                    meta.insert("task_id".to_string(), task.id.clone());
                    meta.insert("status".to_string(), "completed".to_string());
                    meta.insert(
                        "duration_ms".to_string(),
                        task_duration.as_millis().to_string(),
                    );
                    meta
                },
            };

            if let Err(e) = state.monitoring_system.publish_event(event).await {
                error!("Failed to publish monitoring event: {}", e);
            }

            if let Err(e) = state.task_storage.update_task(task).await {
                error!("Failed to update task: {}", e);
                metrics_collector.record_storage_operation(false);
            } else {
                metrics_collector.record_storage_operation(true);
            }
        }
        Err(e) => {
            error!("Task {} failed: {}", task.id, e);

            task.error = Some(e.to_string());
            task.status = TaskStatus::Failed;
            task.completed_at = Some(chrono::Utc::now());

            #[cfg(feature = "nats")]
            state
                .publish_task_status(&task.id, "failed", None, Some(&e.to_string()))
                .await;

            #[cfg(feature = "nats")]
            if task.tags.get("type").map(|s| s.as_str()) == Some("component") {
                let event = provisioning_orchestrator::ComponentLifecycleEvent {
                    task_id: task.id.clone(),
                    component: task.tags.get("component").cloned().unwrap_or_default(),
                    server:    task.tags.get("server").cloned().unwrap_or_default(),
                    workspace: task.tags.get("workspace").cloned().unwrap_or_default(),
                    operation: task.tags.get("operation").cloned().unwrap_or_default(),
                    status:    "failed".to_string(),
                };
                state.publish_component_lifecycle(&event).await;
            }

            // Record metrics
            metrics_collector.record_task_failure();

            // Publish monitoring event
            let event = MonitoringEvent {
                event_type: MonitoringEventType::TaskStatusChanged,
                timestamp: chrono::Utc::now(),
                data: serde_json::to_value(&task).unwrap_or_default(),
                metadata: {
                    let mut meta = std::collections::HashMap::new();
                    meta.insert("task_id".to_string(), task.id.clone());
                    meta.insert("status".to_string(), "failed".to_string());
                    meta.insert("error".to_string(), e.to_string());
                    meta
                },
            };

            if let Err(e) = state.monitoring_system.publish_event(event).await {
                error!("Failed to publish monitoring event: {}", e);
            }

            if let Err(e) = state.task_storage.update_task(task.clone()).await {
                error!("Failed to update task: {}", e);
                metrics_collector.record_storage_operation(false);
            } else {
                metrics_collector.record_storage_operation(true);
            }

            // Try to requeue for retry
            if let Err(e) = state.task_storage.requeue_failed_task(&task.id).await {
                error!("Failed to requeue task: {}", e);
                metrics_collector.record_storage_operation(false);
            }
        }
    }
}

pub async fn process_tasks(state: SharedState) {
    info!("Starting task processor (max 4 concurrent tasks)");

    // Semaphore limits concurrent task executions; acquire BEFORE dequeue so we
    // never pull a task we can't immediately run.
    let semaphore = Arc::new(tokio::sync::Semaphore::new(4));

    loop {
        let permit = match Arc::clone(&semaphore).acquire_owned().await {
            Ok(p) => p,
            Err(_) => break,
        };

        match state.task_storage.dequeue().await {
            Ok(Some(task)) => {
                let state_clone = state.clone();
                tokio::spawn(async move {
                    let _permit = permit; // released when this task completes
                    execute_task(state_clone, task).await;
                });
            }
            Ok(None) => {
                drop(permit);
                tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
            }
            Err(e) => {
                drop(permit);
                error!("Error dequeuing task: {}", e);
                tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
            }
        }
    }
}
