// Tests for mecha10_cli::services::docker

use mecha10_cli::services::docker::*;

use std::path::PathBuf;
use tempfile::TempDir;

// === Service Creation Tests ===

#[test]
fn test_new_service() {
    let service = DockerService::new();
    assert!(service.compose_file.is_none());
}

#[test]
fn test_with_compose_file() {
    let service = DockerService::with_compose_file("custom-compose.yml");
    assert_eq!(service.compose_file, Some("custom-compose.yml".to_string()));
}

#[test]
fn test_with_compose_file_string() {
    let service = DockerService::with_compose_file(String::from("path/to/compose.yml"));
    assert_eq!(service.compose_file, Some("path/to/compose.yml".to_string()));
}

#[test]
fn test_with_compose_file_pathbuf() {
    let path = PathBuf::from("config/docker-compose.yml");
    let service = DockerService::with_compose_file(path.to_string_lossy().to_string());
    assert_eq!(service.compose_file, Some("config/docker-compose.yml".to_string()));
}

#[test]
fn test_with_compose_file_absolute_path() {
    let service = DockerService::with_compose_file("/etc/docker/compose.yml");
    assert_eq!(service.compose_file, Some("/etc/docker/compose.yml".to_string()));
}

#[test]
fn test_with_compose_file_relative_path() {
    let service = DockerService::with_compose_file("./docker-compose.yml");
    assert_eq!(service.compose_file, Some("./docker-compose.yml".to_string()));
}

#[test]
fn test_default_trait() {
    let service = DockerService::default();
    assert!(service.compose_file.is_none());
}

#[test]
fn test_default_equals_new() {
    let service1 = DockerService::new();
    let service2 = DockerService::default();
    assert_eq!(service1.compose_file, service2.compose_file);
}

// === File Existence Tests ===

#[test]
fn test_compose_file_exists_with_temp_file() {
    let temp_dir = TempDir::new().unwrap();
    let compose_path = temp_dir.path().join("docker-compose.yml");
    std::fs::write(&compose_path, "version: '3'").unwrap();

    let service = DockerService::new();
    assert!(service.compose_file_exists(Some(temp_dir.path())));
}

#[test]
fn test_compose_file_exists_missing() {
    let temp_dir = TempDir::new().unwrap();

    let service = DockerService::new();
    assert!(!service.compose_file_exists(Some(temp_dir.path())));
}

#[test]
fn test_compose_file_exists_with_custom_file() {
    let temp_dir = TempDir::new().unwrap();
    let custom_compose = temp_dir.path().join("custom.yml");
    std::fs::write(&custom_compose, "version: '3'").unwrap();

    let service = DockerService::with_compose_file(custom_compose.to_string_lossy().to_string());
    // When checking with None and a custom file is set, it should check the custom file
    assert!(service.compose_file_exists(None)); // Custom file exists
}

#[test]
fn test_compose_file_exists_in_current_dir() {
    let service = DockerService::new();
    // This will check for docker-compose.yml in current directory
    // Result depends on where tests are run from
    let _ = service.compose_file_exists(None);
}

#[test]
fn test_compose_file_exists_nested_path() {
    let temp_dir = TempDir::new().unwrap();
    let nested = temp_dir.path().join("config").join("deploy");
    std::fs::create_dir_all(&nested).unwrap();
    let compose_path = nested.join("docker-compose.yml");
    std::fs::write(&compose_path, "version: '3'").unwrap();

    let service = DockerService::new();
    assert!(service.compose_file_exists(Some(&nested)));
}

// === Installation Check Tests ===
// Note: These tests require Docker to be installed

#[test]
fn test_check_installation() {
    let service = DockerService::new();
    // This test will pass or fail depending on whether Docker is installed
    let _ = service.check_installation();
}

#[test]
fn test_check_daemon() {
    let service = DockerService::new();
    // This test will pass or fail depending on whether Docker daemon is running
    let _ = service.check_daemon();
}

// === Service Method Structure Tests ===
// These test that the methods exist and have the correct signatures

#[test]
fn test_service_has_compose_build_method() {
    let service = DockerService::new();
    // Just verifying the method exists with correct signature
    let _result: Result<()> = service.compose_build(None);
}

#[test]
fn test_service_has_compose_build_with_service() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_build(Some("redis"));
}

#[tokio::test]
async fn test_service_has_compose_up_method() {
    let service = DockerService::new();
    // Just verifying the method exists with correct signature
    let _result: Result<()> = service.compose_up(false).await;
}

#[tokio::test]
async fn test_service_has_compose_up_service_method() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_up_service("redis", true).await;
}

#[tokio::test]
async fn test_service_has_compose_stop_method() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_stop(None).await;
}

#[tokio::test]
async fn test_service_has_compose_down_method() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_down().await;
}

#[tokio::test]
async fn test_service_has_compose_restart_method() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_restart(None).await;
}

#[test]
fn test_service_has_compose_logs_method() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_logs(None, false, None);
}

#[test]
fn test_service_has_compose_logs_with_options() {
    let service = DockerService::new();
    let _result: Result<()> = service.compose_logs(Some("redis"), true, Some(100));
}

#[tokio::test]
async fn test_service_has_is_container_running_method() {
    let service = DockerService::new();
    let _result: Result<bool> = service.is_container_running("test-container").await;
}

#[tokio::test]
async fn test_service_has_list_containers_method() {
    let service = DockerService::new();
    let _result: Result<Vec<ContainerInfo>> = service.list_containers().await;
}

#[tokio::test]
async fn test_service_has_wait_for_healthy_method() {
    let service = DockerService::new();
    let _result: Result<()> = service.wait_for_healthy("test", Duration::from_secs(1)).await;
}

// === Multiple Service Instances Tests ===

#[test]
fn test_multiple_services_independent() {
    let service1 = DockerService::with_compose_file("compose1.yml");
    let service2 = DockerService::with_compose_file("compose2.yml");

    assert_eq!(service1.compose_file, Some("compose1.yml".to_string()));
    assert_eq!(service2.compose_file, Some("compose2.yml".to_string()));
}

#[test]
fn test_service_compose_file_immutable() {
    let service = DockerService::with_compose_file("original.yml");
    assert_eq!(service.compose_file, Some("original.yml".to_string()));

    // Create a new service to "change" the file
    let service2 = DockerService::with_compose_file("changed.yml");
    assert_eq!(service2.compose_file, Some("changed.yml".to_string()));

    // Original should be unchanged
    assert_eq!(service.compose_file, Some("original.yml".to_string()));
}

// === Edge Cases ===

#[test]
fn test_with_empty_compose_file_path() {
    let service = DockerService::with_compose_file("");
    assert_eq!(service.compose_file, Some(String::new()));
}

#[test]
fn test_compose_file_with_special_characters() {
    let service = DockerService::with_compose_file("docker-compose (1).yml");
    assert_eq!(service.compose_file, Some("docker-compose (1).yml".to_string()));
}

#[test]
fn test_compose_file_with_unicode() {
    let service = DockerService::with_compose_file("docker-撰写.yml");
    assert_eq!(service.compose_file, Some("docker-撰写.yml".to_string()));
}
