# Runtime abstraction module for Docker, Podman, OrbStack, Colima, nerdctl
#
# Provides unified interface for container runtime operations across platforms.
# Follows NUSHELL_GUIDELINES.md: single purpose, explicit types, early return, atomic operations

# Detect available container runtime on the system
#
# Returns: record with runtime info
# Errors: propagates if no runtime found
export def runtime-detect [] {
    let runtimes = [
        { name: "docker", command: "docker", priority: 1 }
        { name: "podman", command: "podman", priority: 2 }
        { name: "orbstack", command: "orbctl", priority: 3 }
        { name: "colima", command: "colima", priority: 4 }
        { name: "nerdctl", command: "nerdctl", priority: 5 }
    ]

    let available = (
        $runtimes
        | each {|rt|
            let exists = (^which $rt.command 2>/dev/null | length) > 0
            {
                name: $rt.name
                command: $rt.command
                priority: $rt.priority
                available: $exists
            }
        }
        | where available
        | sort-by priority
    )

    if ($available | length) == 0 {
        error "No container runtime detected. Install Docker, Podman, or another supported runtime."
    }

    $available | first
}

# Execute command in detected runtime
#
# Parameters:
#   command: string - Command to execute
#   --check: bool - Dry-run mode (no execution)
#
# Returns: string - Command output
# Errors: propagates from command execution
export def runtime-exec [command: string, --check = false] {
    # Validate inputs early
    if ($command | str trim) == "" {
        error "Command cannot be empty"
    }

    let runtime = (runtime-detect)
    let full_command = ($"($runtime.command) ($command)")

    if $check {
        return $"Would execute: [$full_command]"
    }

    # Execute atomically - succeed or fail completely
    let result = (
        do {
            ^sh -c $full_command
        } | complete
    )

    if $result.exit_code == 0 {
        $result.stdout
    } else {
        error $"Runtime execution failed: [$runtime.name]\nCommand: [$command]\nError: [$result.stderr]"
    }
}

# Adapt docker-compose file for detected runtime
#
# Parameters:
#   file_path: string - Path to compose file
#
# Returns: string - Compose command for this runtime
# Errors: propagates if file not found or runtime not available
export def runtime-compose [file_path: string] {
    # Validate input early
    if (not ($file_path | path exists)) {
        error $"Compose file not found: [$file_path]"
    }

    let runtime = (runtime-detect)

    # Generate appropriate compose command based on runtime
    match $runtime.name {
        "docker" => { $"docker compose -f [$file_path]" }
        "podman" => { $"podman compose -f [$file_path]" }
        "orbstack" | "colima" => { $"docker compose -f [$file_path]" }
        "nerdctl" => { $"nerdctl compose -f [$file_path]" }
        _ => { error $"Unknown runtime: [$runtime.name]" }
    }
}

# Get runtime information
#
# Returns: record - Runtime details
# Errors: propagates if no runtime available
export def runtime-info [] {
    let rt = (runtime-detect)

    {
        name: $rt.name
        command: $rt.command
        available: true
        version: (
            let result = (do {
                let ver_output = (^sh -c $"($rt.command) --version" 2>&1)
                $ver_output | str trim | str substring [0..<40]
            } | complete)

            if $result.exit_code != 0 {
                "unknown"
            } else {
                $result.stdout
            }
        )
    }
}

# List all available runtimes on the system
#
# Returns: table - All available runtimes
# Errors: none (returns empty if none available)
export def runtime-list [] {
    let runtimes = [
        { name: "docker", command: "docker" }
        { name: "podman", command: "podman" }
        { name: "orbstack", command: "orbctl" }
        { name: "colima", command: "colima" }
        { name: "nerdctl", command: "nerdctl" }
    ]

    $runtimes
    | each {|rt|
        let exists = (^which $rt.command 2>/dev/null | length) > 0
        {
            name: $rt.name
            command: $rt.command
            available: $exists
        }
    }
    | where available
}

#[cfg(test)]
# Tests for runtime module
def test-runtime-detect [] {
    # Note: Tests require runtime to be installed
    let result = (do { runtime-detect } | complete)
    let rt = if $result.exit_code != 0 { null } else { $result.stdout }
    if ($rt != null) {
        assert ($rt.name != "")
    }
}

def test-runtime-info [] {
    let result = (do { runtime-info } | complete)
    let info = if $result.exit_code != 0 { null } else { $result.stdout }
    if ($info != null) {
        assert ($info.name != "")
    }
}

def test-runtime-list [] {
    let list = (runtime-list)
    # Should return list of available runtimes (0 or more)
    assert (($list | length) >= 0)
}
