use chrono::Utc;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

#[derive(Debug, Serialize, Deserialize)]
pub struct TaskInfo {
    pub id: String,
    pub status: String,
    pub created_at: String,
    pub priority: u8,
    pub workflow_id: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct OrchStatus {
    pub running: bool,
    pub tasks_pending: usize,
    pub tasks_running: usize,
    pub tasks_completed: usize,
    pub last_check: String,
}

pub fn get_orchestrator_data_dir() -> PathBuf {
    std::env::var("ORCHESTRATOR_DATA_DIR")
        .map(PathBuf::from)
        .unwrap_or_else(|_| PathBuf::from("provisioning/platform/orchestrator/data"))
}

pub fn read_local_status(data_dir: &Path) -> Result<OrchStatus, String> {
    let status_file = data_dir.join("status.json");

    if !status_file.exists() {
        // Return default status if file doesn't exist
        return Ok(OrchStatus {
            running: false,
            tasks_pending: 0,
            tasks_running: 0,
            tasks_completed: 0,
            last_check: Utc::now().to_rfc3339(),
        });
    }

    let content = fs::read_to_string(&status_file)
        .map_err(|e| format!("Failed to read status file: {}", e))?;

    serde_json::from_str(&content).map_err(|e| format!("Failed to parse status: {}", e))
}

pub fn read_task_queue(
    data_dir: &Path,
    status_filter: Option<String>,
    limit: Option<i64>,
) -> Result<Vec<TaskInfo>, String> {
    let tasks_dir = data_dir.join("tasks");

    if !tasks_dir.exists() {
        return Ok(vec![]);
    }

    let mut tasks = Vec::new();

    for entry in WalkDir::new(&tasks_dir)
        .max_depth(2)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
    {
        let content = fs::read_to_string(entry.path())
            .map_err(|e| format!("Failed to read task file: {}", e))?;

        let task: TaskInfo =
            serde_json::from_str(&content).map_err(|e| format!("Failed to parse task: {}", e))?;

        // Apply status filter
        if let Some(ref filter) = status_filter {
            if &task.status != filter {
                continue;
            }
        }

        tasks.push(task);
    }

    // Sort by priority (descending) and created_at (ascending)
    tasks.sort_by(|a, b| {
        b.priority
            .cmp(&a.priority)
            .then_with(|| a.created_at.cmp(&b.created_at))
    });

    // Apply limit
    if let Some(lim) = limit {
        tasks.truncate(lim as usize);
    }

    Ok(tasks)
}

pub fn validate_kcl_workflow(
    workflow_path: &str,
    strict: bool,
) -> Result<ValidationResult, String> {
    use std::process::Command;

    // Check if file exists
    if !PathBuf::from(workflow_path).exists() {
        return Ok(ValidationResult {
            valid: false,
            errors: vec![format!("File not found: {}", workflow_path)],
            warnings: vec![],
        });
    }

    // Run KCL validation
    let output = Command::new("kcl")
        .arg("vet")
        .arg(workflow_path)
        .output()
        .map_err(|e| format!("Failed to run kcl: {}", e))?;

    let _stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);

    let mut errors = Vec::new();
    let mut warnings = Vec::new();

    if !output.status.success() {
        // Parse errors from stderr
        for line in stderr.lines() {
            if line.contains("error") || line.contains("Error") {
                errors.push(line.to_string());
            } else if line.contains("warning") || line.contains("Warning") {
                warnings.push(line.to_string());
            }
        }
    }

    // Additional strict checks
    if strict {
        let content = fs::read_to_string(workflow_path)
            .map_err(|e| format!("Failed to read workflow: {}", e))?;

        // Check for required fields
        if !content.contains("name") {
            warnings.push("Missing 'name' field".to_string());
        }
        if !content.contains("version") {
            warnings.push("Missing 'version' field".to_string());
        }
        if !content.contains("operations") {
            errors.push("Missing 'operations' field (required)".to_string());
        }
    }

    Ok(ValidationResult {
        valid: errors.is_empty(),
        errors,
        warnings,
    })
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ValidationResult {
    pub valid: bool,
    pub errors: Vec<String>,
    pub warnings: Vec<String>,
}

// Helper to check if orchestrator is running
pub fn is_orchestrator_running() -> bool {
    use std::process::Command;

    // Try to connect to orchestrator health endpoint
    Command::new("curl")
        .arg("-s")
        .arg("-m")
        .arg("2")
        .arg("http://localhost:9090/health")
        .output()
        .map(|o| o.status.success())
        .unwrap_or(false)
}

/// Get a task by ID from the task queue
pub fn get_task_by_id(data_dir: &Path, task_id: &str) -> Result<TaskInfo, String> {
    let tasks_dir = data_dir.join("tasks");

    if !tasks_dir.exists() {
        return Err(format!("Tasks directory not found: {:?}", tasks_dir));
    }

    // Search for the task file
    for entry in WalkDir::new(&tasks_dir)
        .max_depth(2)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
    {
        let content = fs::read_to_string(entry.path())
            .map_err(|e| format!("Failed to read task file: {}", e))?;

        let task: TaskInfo =
            serde_json::from_str(&content).map_err(|e| format!("Failed to parse task: {}", e))?;

        if task.id == task_id {
            return Ok(task);
        }
    }

    Err(format!("Task not found: {}", task_id))
}

/// Submit a workflow to the orchestrator queue
pub fn submit_workflow(data_dir: &Path, workflow_path: &str, priority: u8) -> Result<String, String> {
    use uuid::Uuid;

    let tasks_dir = data_dir.join("tasks").join("pending");

    // Create tasks directory if it doesn't exist
    fs::create_dir_all(&tasks_dir)
        .map_err(|e| format!("Failed to create tasks directory: {}", e))?;

    // Generate task ID
    let task_id = format!("task-{}", Uuid::new_v4().to_string().split('-').next().unwrap_or("0000"));

    // Read workflow file to get workflow_id
    let workflow_content = fs::read_to_string(workflow_path)
        .map_err(|e| format!("Failed to read workflow file: {}", e))?;

    // Extract workflow name from content (simple extraction)
    let workflow_id = workflow_content
        .lines()
        .find(|l| l.contains("name"))
        .and_then(|l| l.split('=').nth(1))
        .map(|s| s.trim().trim_matches('"').to_string())
        .unwrap_or_else(|| workflow_path.to_string());

    // Create task info
    let task = TaskInfo {
        id: task_id.clone(),
        status: "pending".to_string(),
        created_at: Utc::now().to_rfc3339(),
        priority,
        workflow_id: Some(workflow_id),
    };

    // Write task file
    let task_file = tasks_dir.join(format!("{}.json", task_id));
    let task_json = serde_json::to_string_pretty(&task)
        .map_err(|e| format!("Failed to serialize task: {}", e))?;

    fs::write(&task_file, task_json)
        .map_err(|e| format!("Failed to write task file: {}", e))?;

    Ok(task_id)
}

/// Result of monitoring a task
#[derive(Debug, Serialize, Deserialize)]
pub struct MonitorResult {
    pub id: String,
    pub status: String,
    pub duration_ms: u64,
    pub message: Option<String>,
}

/// Monitor a task until completion or timeout
pub fn monitor_task(
    data_dir: &Path,
    task_id: &str,
    interval_ms: u64,
    timeout_secs: u64,
) -> Result<MonitorResult, String> {
    use std::thread;
    use std::time::{Duration, Instant};

    let start = Instant::now();
    let timeout = Duration::from_secs(timeout_secs);
    let interval = Duration::from_millis(interval_ms);

    loop {
        // Check if timed out
        if start.elapsed() > timeout {
            return Err(format!(
                "Timeout waiting for task {} after {} seconds",
                task_id, timeout_secs
            ));
        }

        // Get current task status
        match get_task_by_id(data_dir, task_id) {
            Ok(task) => {
                // Check if task is complete
                let is_completed = task.status == "completed";
                let is_failed = task.status == "failed";
                if is_completed || is_failed {
                    let message = if is_completed {
                        Some("Task completed successfully".to_string())
                    } else {
                        Some("Task failed".to_string())
                    };
                    return Ok(MonitorResult {
                        id: task.id,
                        status: task.status,
                        duration_ms: start.elapsed().as_millis() as u64,
                        message,
                    });
                }
            }
            Err(e) => {
                // Task might have been moved to another status directory
                // Continue polling
                eprintln!("Warning: {}", e);
            }
        }

        // Wait before next poll
        thread::sleep(interval);
    }
}
