# Taskserv Testing Framework
# Provides sandbox testing capabilities for taskservs

# REMOVED: use lib_provisioning * - causes circular import
use utils.nu *
use validate.nu *
use deps_validator.nu *
use ../lib_provisioning/config/accessor.nu *

# Test taskserv in local container sandbox
export def "main test" [
    taskserv_name: string
    --infra (-i): string
    --settings (-s): string
    --runtime: string = "docker"  # docker, podman, or native
    --verbose (-v)
    --keep             # Keep container after test
    --out: string
] {
    if ($out | is-not-empty) {
        set-provisioning-out $out
        set-provisioning-no-terminal true
    }

    # Load settings
    let result = (do {
        find_get_settings --infra $infra --settings $settings
    } | complete)

    if $result.exit_code != 0 {
        _print $"🛑 Failed to load settings"
        return
    }

    let curr_settings = $result.stdout

    _print $"\n(_ansi cyan_bold)Taskserv Sandbox Testing(_ansi reset)"
    _print $"Taskserv: (_ansi yellow_bold)($taskserv_name)(_ansi reset)"
    _print $"Runtime: ($runtime)\n"

    # Check runtime availability
    let runtime_check = (check-runtime $runtime)
    if not $runtime_check.available {
        _print $"🛑 Runtime ($runtime) not available: ($runtime_check.error)"
        return
    }

    if $verbose {
        _print $"(_ansi green)✓ Runtime ($runtime) available(_ansi reset)"
    }

    # Run validation first
    _print $"\n(_ansi yellow)→ Running pre-test validation...(_ansi reset)"
    let validation = (validate-dependencies $taskserv_name $curr_settings --verbose=$verbose)

    if not $validation.valid {
        _print $"(_ansi red)✗ Validation failed, cannot proceed with testing(_ansi reset)"
        print-validation-report $validation
        return
    }

    if $verbose {
        _print $"(_ansi green)✓ Validation passed(_ansi reset)"
    }

    # Prepare sandbox environment
    _print $"\n(_ansi yellow)→ Preparing sandbox environment...(_ansi reset)"
    let sandbox = (prepare-sandbox $taskserv_name $runtime $verbose)

    if not $sandbox.success {
        _print $"🛑 Failed to prepare sandbox: ($sandbox.error)"
        return
    }

    _print $"(_ansi green)✓ Sandbox prepared: ($sandbox.container_id)(_ansi reset)"

    # Run tests in sandbox
    _print $"\n(_ansi yellow)→ Running tests in sandbox...(_ansi reset)"
    let test_result = (run-sandbox-tests $taskserv_name $sandbox $curr_settings $verbose)

    # Cleanup if requested
    if not $keep {
        _print $"\n(_ansi yellow)→ Cleaning up sandbox...(_ansi reset)"
        cleanup-sandbox $sandbox $runtime
    } else {
        _print $"\n(_ansi blue)ℹ Sandbox container kept: ($sandbox.container_id)(_ansi reset)"
        _print $"  Connect with: ($runtime) exec -it ($sandbox.container_id) bash"
    }

    # Print summary
    print-test-summary $test_result
}

# Check if runtime is available
def check-runtime [
    runtime: string
] {
    match $runtime {
        "docker" => {
            let available = (which docker | length) > 0
            if $available {
                let docker_result = (do {
                    ^docker ps
                } | complete)

                let running = if $docker_result.exit_code == 0 {
                    $docker_result.exit_code
                } else {
                    1
                }

                if $running == 0 {
                    { available: true, error: "" }
                } else {
                    { available: false, error: "Docker daemon not running" }
                }
            } else {
                { available: false, error: "Docker not installed" }
            }
        }
        "podman" => {
            let available = (which podman | length) > 0
            if $available {
                { available: true, error: "" }
            } else {
                { available: false, error: "Podman not installed" }
            }
        }
        "native" => {
            { available: true, error: "" }
        }
        _ => {
            { available: false, error: $"Unknown runtime: ($runtime)" }
        }
    }
}

# Prepare sandbox environment
def prepare-sandbox [
    taskserv_name: string
    runtime: string
    verbose: bool
] {
    if $runtime == "native" {
        return {
            success: true
            container_id: "native"
            runtime: "native"
            error: ""
        }
    }

    # Select appropriate base image based on taskserv requirements
    let base_image = "ubuntu:22.04"  # Default, could be made configurable

    if $verbose {
        _print $"  Using base image: ($base_image)"
    }

    # Create container
    let container_result = match $runtime {
        "docker" => {
            do {
                ^docker run -d --rm --name $"taskserv-test-($taskserv_name)" $base_image sleep 3600
            } | complete
        }
        "podman" => {
            do {
                ^podman run -d --rm --name $"taskserv-test-($taskserv_name)" $base_image sleep 3600
            } | complete
        }
    }

    if $container_result.exit_code != 0 {
        return {
            success: false
            container_id: ""
            runtime: $runtime
            error: $"Container creation failed: ($container_result.stderr)"
        }
    }

    let container_id = ($container_result.stdout | str trim)

    return {
        success: true
        container_id: $container_id
        runtime: $runtime
        base_image: $base_image
        error: ""
    }
}

# Run tests in sandbox
def run-sandbox-tests [
    taskserv_name: string
    sandbox: record
    settings: record
    verbose: bool
] {
    mut test_results = []

    # Test 1: Check if required packages can be installed
    _print $"  Test 1: Package prerequisites..."
    let pkg_test = (test-package-prerequisites $taskserv_name $sandbox $verbose)
    $test_results = ($test_results | append $pkg_test)

    # Test 2: Check if configuration files are valid
    _print $"  Test 2: Configuration validity..."
    let config_test = (test-configuration-validity $taskserv_name $sandbox $verbose)
    $test_results = ($test_results | append $config_test)

    # Test 3: Check if scripts can be executed
    _print $"  Test 3: Script execution..."
    let script_test = (test-script-execution $taskserv_name $sandbox $verbose)
    $test_results = ($test_results | append $script_test)

    # Test 4: Health check simulation (if available)
    _print $"  Test 4: Health check simulation..."
    let health_test = (test-health-check $taskserv_name $sandbox $settings $verbose)
    $test_results = ($test_results | append $health_test)

    let total_passed = ($test_results | where status == "passed" | length)
    let total_failed = ($test_results | where status == "failed" | length)
    let total_skipped = ($test_results | where status == "skipped" | length)

    return {
        taskserv: $taskserv_name
        tests: $test_results
        summary: {
            total: ($test_results | length)
            passed: $total_passed
            failed: $total_failed
            skipped: $total_skipped
        }
        overall_passed: ($total_failed == 0)
    }
}

# Test package prerequisites
def test-package-prerequisites [
    taskserv_name: string
    sandbox: record
    verbose: bool
] {
    if $sandbox.runtime == "native" {
        return {
            test: "Package prerequisites"
            status: "skipped"
            message: "Skipped in native mode"
        }
    }

    # This is a simplified test - would need expansion
    let result = match $sandbox.runtime {
        "docker" => {
            do {
                ^docker exec $sandbox.container_id apt-get update
            } | complete
        }
        "podman" => {
            do {
                ^podman exec $sandbox.container_id apt-get update
            } | complete
        }
    }

    if $result.exit_code != 0 {
        return {
            test: "Package prerequisites"
            status: "failed"
            message: $"Failed to update package list: ($result.stderr)"
        }
    }

    if $result.exit_code == 0 {
        {
            test: "Package prerequisites"
            status: "passed"
            message: "Package manager accessible"
        }
    } else {
        {
            test: "Package prerequisites"
            status: "failed"
            message: $"Package update failed: ($result.stderr)"
        }
    }
}

# Test configuration validity
def test-configuration-validity [
    taskserv_name: string
    sandbox: record
    verbose: bool
] {
    # Run Nickel validation
    let decl_result = (validate-nickel-schemas $taskserv_name --verbose=false)

    if $decl_result.valid {
        {
            test: "Configuration validity"
            status: "passed"
            message: $"($decl_result.files_checked) configuration files validated"
        }
    } else {
        {
            test: "Configuration validity"
            status: "failed"
            message: $"Nickel validation failed: (($decl_result.errors | str join ', '))"
        }
    }
}

# Test script execution
def test-script-execution [
    taskserv_name: string
    sandbox: record
    verbose: bool
] {
    # Run script validation
    let script_result = (validate-scripts $taskserv_name --verbose=false)

    if $script_result.valid {
        {
            test: "Script execution"
            status: "passed"
            message: $"($script_result.files_checked) scripts validated"
        }
    } else {
        {
            test: "Script execution"
            status: "failed"
            message: $"Script validation failed: (($script_result.errors | str join ', '))"
        }
    }
}

# Test health check
def test-health-check [
    taskserv_name: string
    sandbox: record
    settings: record
    verbose: bool
] {
    let health_validation = (validate-health-check $taskserv_name $settings --verbose=false)

    if not $health_validation.has_health_check {
        return {
            test: "Health check"
            status: "skipped"
            message: "No health check defined"
        }
    }

    if $health_validation.valid {
        {
            test: "Health check"
            status: "passed"
            message: $"Health check configuration valid: ($health_validation.endpoint)"
        }
    } else {
        {
            test: "Health check"
            status: "failed"
            message: $"Health check validation failed: (($health_validation.errors | str join ', '))"
        }
    }
}

# Cleanup sandbox
def cleanup-sandbox [
    sandbox: record
    runtime: string
] {
    if $sandbox.runtime == "native" {
        return
    }

    let cleanup_result = match $runtime {
        "docker" => {
            do {
                ^docker stop $sandbox.container_id
            } | complete
        }
        "podman" => {
            do {
                ^podman stop $sandbox.container_id
            } | complete
        }
    }

    if $cleanup_result.exit_code == 0 {
        _print $"(_ansi green)✓ Sandbox cleaned up(_ansi reset)"
    } else {
        _print $"(_ansi yellow)⚠ Warning: Failed to cleanup sandbox: ($cleanup_result.stderr)(_ansi reset)"
    }
}

# Print test summary
def print-test-summary [
    results: record
] {
    _print $"\n(_ansi cyan_bold)Test Summary(_ansi reset)"
    _print $"Total tests: ($results.summary.total)"
    _print $"(_ansi green)Passed: ($results.summary.passed)(_ansi reset)"
    _print $"(_ansi red)Failed: ($results.summary.failed)(_ansi reset)"
    _print $"(_ansi yellow)Skipped: ($results.summary.skipped)(_ansi reset)"

    _print $"\n(_ansi cyan)Detailed Results:(_ansi reset)"
    for test in $results.tests {
        let icon = match $test.status {
            "passed" => $"(_ansi green)✓(_ansi reset)"
            "failed" => $"(_ansi red)✗(_ansi reset)"
            "skipped" => $"(_ansi yellow)⊘(_ansi reset)"
            _ => "•"
        }
        _print $"  ($icon) ($test.test): ($test.message)"
    }

    if $results.overall_passed {
        _print $"\n(_ansi green_bold)✓ All tests passed(_ansi reset)"
    } else {
        _print $"\n(_ansi red_bold)✗ Some tests failed(_ansi reset)"
    }
}
