//! Unit tests for the orchestrator plugin.

use crate::error::{OrchestratorError, OrchestratorErrorKind};
use crate::helpers;
use std::path::PathBuf;

// =============================================================================
// Error Module Tests
// =============================================================================

#[test]
fn test_orchestrator_error_display() {
    let error = OrchestratorError::new(OrchestratorErrorKind::TaskNotFound, "task-123");
    let display = format!("{}", error);
    assert!(display.contains("task not found"));
    assert!(display.contains("task-123"));
}

#[test]
fn test_orchestrator_error_with_source() {
    let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
    let error = OrchestratorError::with_source(
        OrchestratorErrorKind::FileError,
        "failed to read task file",
        io_error,
    );
    assert!(error.to_string().contains("caused by"));
}

#[test]
fn test_orchestrator_error_kind_display() {
    assert_eq!(OrchestratorErrorKind::TaskNotFound.to_string(), "task not found");
    assert_eq!(OrchestratorErrorKind::WorkflowNotFound.to_string(), "workflow not found");
    assert_eq!(OrchestratorErrorKind::ValidationFailed.to_string(), "validation failed");
    assert_eq!(OrchestratorErrorKind::SubmissionFailed.to_string(), "submission failed");
    assert_eq!(OrchestratorErrorKind::FileError.to_string(), "file error");
    assert_eq!(OrchestratorErrorKind::KclError.to_string(), "KCL error");
    assert_eq!(OrchestratorErrorKind::OrchestratorNotRunning.to_string(), "orchestrator not running");
    assert_eq!(OrchestratorErrorKind::Timeout.to_string(), "timeout");
}

#[test]
fn test_orchestrator_error_convenience_constructors() {
    let error = OrchestratorError::task_not_found("test");
    assert_eq!(error.kind, OrchestratorErrorKind::TaskNotFound);

    let error = OrchestratorError::workflow_not_found("test");
    assert_eq!(error.kind, OrchestratorErrorKind::WorkflowNotFound);

    let error = OrchestratorError::validation_failed("test");
    assert_eq!(error.kind, OrchestratorErrorKind::ValidationFailed);

    let error = OrchestratorError::submission_failed("test");
    assert_eq!(error.kind, OrchestratorErrorKind::SubmissionFailed);

    let error = OrchestratorError::kcl_error("test");
    assert_eq!(error.kind, OrchestratorErrorKind::KclError);

    let error = OrchestratorError::timeout("test");
    assert_eq!(error.kind, OrchestratorErrorKind::Timeout);
}

#[test]
fn test_orchestrator_error_to_labeled_error() {
    let error = OrchestratorError::new(OrchestratorErrorKind::TaskNotFound, "test error");
    let labeled: nu_protocol::LabeledError = error.into();
    assert!(format!("{:?}", labeled).contains("test error"));
}

// =============================================================================
// Helper Module Tests
// =============================================================================

#[test]
fn test_get_orchestrator_data_dir() {
    let dir = helpers::get_orchestrator_data_dir();
    // Should return a path containing orchestrator
    assert!(dir.to_string_lossy().contains("orchestrator"));
}

#[test]
fn test_read_local_status_missing_dir() {
    let non_existent = PathBuf::from("/non/existent/path");
    let result = helpers::read_local_status(&non_existent);
    // Should return default status if directory doesn't exist
    assert!(result.is_ok());
    let status = result.unwrap();
    assert!(!status.running);
}

#[test]
fn test_read_task_queue_missing_dir() {
    let non_existent = PathBuf::from("/non/existent/path");
    let result = helpers::read_task_queue(&non_existent, None, None);
    // Should return empty list if directory doesn't exist
    assert!(result.is_ok());
    assert!(result.unwrap().is_empty());
}

#[test]
fn test_get_task_by_id_missing_dir() {
    let non_existent = PathBuf::from("/non/existent/path");
    let result = helpers::get_task_by_id(&non_existent, "task-123");
    assert!(result.is_err());
}

#[test]
fn test_is_orchestrator_running() {
    // This test may return true or false depending on whether orchestrator is running
    // Just verify it doesn't panic
    let _ = helpers::is_orchestrator_running();
}

#[test]
fn test_validation_result() {
    let result = helpers::ValidationResult {
        valid: true,
        errors: vec![],
        warnings: vec!["minor warning".to_string()],
    };
    assert!(result.valid);
    assert!(result.errors.is_empty());
    assert_eq!(result.warnings.len(), 1);
}

#[test]
fn test_task_info_serialization() {
    let task = helpers::TaskInfo {
        id: "task-123".to_string(),
        status: "pending".to_string(),
        created_at: "2024-01-01T00:00:00Z".to_string(),
        priority: 50,
        workflow_id: Some("workflow-1".to_string()),
    };

    let json = serde_json::to_string(&task).expect("Should serialize");
    assert!(json.contains("task-123"));
    assert!(json.contains("pending"));

    let deserialized: helpers::TaskInfo = serde_json::from_str(&json).expect("Should deserialize");
    assert_eq!(deserialized.id, "task-123");
    assert_eq!(deserialized.priority, 50);
}

#[test]
fn test_orch_status_serialization() {
    let status = helpers::OrchStatus {
        running: true,
        tasks_pending: 5,
        tasks_running: 2,
        tasks_completed: 10,
        last_check: "2024-01-01T00:00:00Z".to_string(),
    };

    let json = serde_json::to_string(&status).expect("Should serialize");
    let deserialized: helpers::OrchStatus = serde_json::from_str(&json).expect("Should deserialize");
    assert!(deserialized.running);
    assert_eq!(deserialized.tasks_pending, 5);
}

#[test]
fn test_monitor_result_serialization() {
    let result = helpers::MonitorResult {
        id: "task-123".to_string(),
        status: "completed".to_string(),
        duration_ms: 5000,
        message: Some("Task completed successfully".to_string()),
    };

    let json = serde_json::to_string(&result).expect("Should serialize");
    let deserialized: helpers::MonitorResult = serde_json::from_str(&json).expect("Should deserialize");
    assert_eq!(deserialized.id, "task-123");
    assert_eq!(deserialized.duration_ms, 5000);
}

// =============================================================================
// Plugin Structure Tests
// =============================================================================

#[test]
fn test_plugin_version() {
    use crate::OrchestratorPlugin;
    use nu_plugin::Plugin;

    let plugin = OrchestratorPlugin;
    let version = plugin.version();
    assert!(!version.is_empty());
}

#[test]
fn test_plugin_commands() {
    use crate::OrchestratorPlugin;
    use nu_plugin::Plugin;

    let plugin = OrchestratorPlugin;
    let commands = plugin.commands();

    // Should have 5 commands
    assert_eq!(commands.len(), 5);
}

#[test]
fn test_status_command_signature() {
    use crate::OrchStatus;
    use nu_plugin::SimplePluginCommand;

    let cmd = OrchStatus;
    assert_eq!(SimplePluginCommand::name(&cmd), "orch status");
    assert!(!SimplePluginCommand::examples(&cmd).is_empty());
}

#[test]
fn test_validate_command_signature() {
    use crate::OrchValidate;
    use nu_plugin::SimplePluginCommand;

    let cmd = OrchValidate;
    assert_eq!(SimplePluginCommand::name(&cmd), "orch validate");
    assert!(!SimplePluginCommand::examples(&cmd).is_empty());
}

#[test]
fn test_tasks_command_signature() {
    use crate::OrchTasks;
    use nu_plugin::SimplePluginCommand;

    let cmd = OrchTasks;
    assert_eq!(SimplePluginCommand::name(&cmd), "orch tasks");
    assert!(!SimplePluginCommand::examples(&cmd).is_empty());
}

#[test]
fn test_submit_command_signature() {
    use crate::OrchSubmit;
    use nu_plugin::SimplePluginCommand;

    let cmd = OrchSubmit;
    assert_eq!(SimplePluginCommand::name(&cmd), "orch submit");
    assert!(!SimplePluginCommand::examples(&cmd).is_empty());
}

#[test]
fn test_monitor_command_signature() {
    use crate::OrchMonitor;
    use nu_plugin::SimplePluginCommand;

    let cmd = OrchMonitor;
    assert_eq!(SimplePluginCommand::name(&cmd), "orch monitor");
    assert!(!SimplePluginCommand::examples(&cmd).is_empty());
}

// =============================================================================
// Integration Tests (with tempdir)
// =============================================================================

#[test]
fn test_submit_and_get_task() {
    use tempfile::TempDir;

    // Create a temporary directory
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let data_dir = temp_dir.path();

    // Create a test workflow file
    let workflow_path = data_dir.join("test_workflow.k");
    std::fs::write(&workflow_path, "name = \"test-workflow\"\nversion = \"1.0.0\"\noperations = []")
        .expect("Failed to write workflow");

    // Submit the workflow
    let result = helpers::submit_workflow(
        data_dir,
        workflow_path.to_str().unwrap(),
        50,
    );

    assert!(result.is_ok());
    let task_id = result.unwrap();
    assert!(task_id.starts_with("task-"));

    // Get the task
    let task = helpers::get_task_by_id(data_dir, &task_id);
    assert!(task.is_ok());

    let task = task.unwrap();
    assert_eq!(task.id, task_id);
    assert_eq!(task.status, "pending");
    assert_eq!(task.priority, 50);
}

#[test]
fn test_read_task_queue_with_filter() {
    use tempfile::TempDir;

    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let data_dir = temp_dir.path();
    let tasks_dir = data_dir.join("tasks").join("pending");
    std::fs::create_dir_all(&tasks_dir).expect("Failed to create tasks dir");

    // Create test tasks
    let task1 = helpers::TaskInfo {
        id: "task-1".to_string(),
        status: "pending".to_string(),
        created_at: "2024-01-01T00:00:00Z".to_string(),
        priority: 50,
        workflow_id: None,
    };
    let task2 = helpers::TaskInfo {
        id: "task-2".to_string(),
        status: "running".to_string(),
        created_at: "2024-01-01T00:01:00Z".to_string(),
        priority: 80,
        workflow_id: None,
    };

    std::fs::write(
        tasks_dir.join("task-1.json"),
        serde_json::to_string(&task1).unwrap(),
    ).expect("Failed to write task1");
    std::fs::write(
        tasks_dir.join("task-2.json"),
        serde_json::to_string(&task2).unwrap(),
    ).expect("Failed to write task2");

    // Test without filter
    let tasks = helpers::read_task_queue(data_dir, None, None).unwrap();
    assert_eq!(tasks.len(), 2);

    // Test with status filter
    let tasks = helpers::read_task_queue(data_dir, Some("pending".to_string()), None).unwrap();
    assert_eq!(tasks.len(), 1);
    assert_eq!(tasks[0].status, "pending");

    // Test with limit
    let tasks = helpers::read_task_queue(data_dir, None, Some(1)).unwrap();
    assert_eq!(tasks.len(), 1);
    // Should return highest priority first
    assert_eq!(tasks[0].priority, 80);
}
