# Nested Provisioning System (Phase 4)
#
# Support for nested VMs (VM → VM → Containers).
# Rule 1: Single purpose, Rule 5: Atomic operations

# Selective imports (ADR-025 Phase 3 Layer 2).
use lib_provisioning/vm/lifecycle.nu [vm-info]
use lib_provisioning/vm/volume_management.nu [volume-attach volume-detach]
use lib_provisioning/vm/network_management.nu [network-connect network-disconnect]

export def "nested-vm-create" [
    name: string                                 # VM name
    parent_vm: string                            # Parent VM name
    --cpu: int = 2                               # CPU cores
    --memory: int = 2048                         # Memory in MB
    --disk: int = 20                             # Disk in GB
    --networks: list<string> = []                # Networks to connect
    --volumes: list<string> = []                 # Volumes to attach
    --auto-start = false                         # Auto-start on parent boot
    --nested-virt = true                         # Enable nested virtualization
]: record {
    """
    Create a nested VM inside another VM.

    Enables multi-level virtualization for complex deployments.
    """

    if ($name | is-empty) {
        return {success: false, error: "VM name required"}
    }

    if ($parent_vm | is-empty) {
        return {success: false, error: "Parent VM required"}
    }

    if ($cpu < 1 or $cpu > 32) {
        return {success: false, error: "CPU must be 1-32"}
    }

    if ($memory < 512 or $memory > 65536) {
        return {success: false, error: "Memory must be 512MB-64GB"}
    }

    # Verify parent VM exists
    let parent_info = (vm-info $parent_vm)
    if ("error" in $parent_info) {
        return {success: false, error: "Parent VM not found"}
    }

    # Check nesting depth
    let nesting_depth = (get-nesting-depth $parent_vm)
    if $nesting_depth >= 3 {
        return {success: false, error: "Maximum nesting depth (3) exceeded"}
    }

    let nested_dir = (get-nested-directory)
    bash -c $"mkdir -p ($nested_dir)" | complete

    let nested_meta = {
        name: $name
        parent_vm: $parent_vm
        nesting_depth: ($nesting_depth + 1)
        cpu: $cpu
        memory_mb: $memory
        disk_gb: $disk
        networks: $networks
        volumes: $volumes
        auto_start: $auto_start
        nested_virt: $nested_virt
        created_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        status: "created"
    }

    # Create VM disk (no try-catch)
    let create_result = (do { bash -c $"qemu-img create -f qcow2 ($nested_dir)/($name).qcow2 ($disk)G" } | complete)
    if $create_result.exit_code != 0 {
        return {success: false, error: $"Failed to create VM disk: ($create_result.stderr)"}
    }

    # Save metadata (no try-catch)
    let save_result = (do { bash -c $"cat > ($nested_dir)/($name).json << 'EOF'\n($nested_meta | to json)\nEOF" } | complete)
    if $save_result.exit_code != 0 {
        return {success: false, error: $"Failed to save metadata: ($save_result.stderr)"}
    }

    # Connect to networks (no try-catch)
    $networks | each {|network|
        network-connect $network $name
    }

    # Attach volumes (no try-catch)
    $volumes | each {|volume|
        volume-attach $volume $name
    }

    {
        success: true
        nested_vm: $name
        parent_vm: $parent_vm
        cpu: $cpu
        memory_mb: $memory
        disk_gb: $disk
        nesting_depth: ($nesting_depth + 1)
    }
}

export def "nested-vm-list" [
    parent_vm: string = ""                       # Filter by parent (empty = all)
]: table {
    """
    List nested VMs, optionally filtered by parent.
    """

    let nested_dir = (get-nested-directory)

    if (not ($nested_dir | path exists)) {
        return []
    }

    bash -c $"ls -1 ($nested_dir)/*.json 2>/dev/null"
    | lines
    | each {|file|
        # Guard: Check if file can be opened and parsed as JSON (no try-catch)
        let json_result = (do { open $file | from json } | complete)
        if $json_result.exit_code == 0 {
            let meta = ($json_result.stdout)

            if ($parent_vm | is-empty) or ($meta.parent_vm == $parent_vm) {
                {
                    name: $meta.name
                    parent: $meta.parent_vm
                    depth: $meta.nesting_depth
                    cpu: $meta.cpu
                    memory_mb: $meta.memory_mb
                    disk_gb: $meta.disk_gb
                    status: $meta.status
                    created: $meta.created_at
                }
            }
        } else {
            null
        }
    }
    | compact
}

export def "nested-vm-info" [
    name: string                                 # VM name
]: record {
    """
    Get detailed nested VM information.
    """

    let nested_dir = (get-nested-directory)
    let meta_file = $"($nested_dir)/($name).json"

    if (not ($meta_file | path exists)) {
        return {success: false, error: "Nested VM not found"}
    }

    # Load metadata (no try-catch)
    let meta_result = (do { open $meta_file | from json } | complete)
    if $meta_result.exit_code != 0 {
        return {success: false, error: $"Failed to load metadata: ($meta_result.stderr)"}
    }

    let meta = ($meta_result.stdout)

    {
        success: true
        name: $meta.name
        parent_vm: $meta.parent_vm
        nesting_depth: $meta.nesting_depth
        cpu: $meta.cpu
        memory_mb: $meta.memory_mb
        disk_gb: $meta.disk_gb
        networks: $meta.networks
        volumes: $meta.volumes
        auto_start: $meta.auto_start
        nested_virt: $meta.nested_virt
        created: $meta.created_at
        status: $meta.status
    }
}

export def "nested-vm-delete" [
    name: string                                 # VM name
    --force = false                              # Force delete
]: record {
    """
    Delete a nested VM.
    """

    let nested_dir = (get-nested-directory)
    let meta_file = $"($nested_dir)/($name).json"

    if (not ($meta_file | path exists)) {
        return {success: false, error: "Nested VM not found"}
    }

    # Load metadata (no try-catch)
    let meta_result = (do { open $meta_file | from json } | complete)
    if $meta_result.exit_code != 0 {
        return {success: false, error: $"Failed to load metadata: ($meta_result.stderr)"}
    }

    let meta = ($meta_result.stdout)

    # Detach volumes and networks (no try-catch)
    $meta.volumes | each {|volume|
        volume-detach $volume $name
    }

    $meta.networks | each {|network|
        network-disconnect $network $name
    }

    # Delete VM disk and metadata (no try-catch)
    let rm_disk_result = (do { bash -c $"rm -f ($nested_dir)/($name).qcow2" } | complete)
    if $rm_disk_result.exit_code != 0 {
        return {success: false, error: $"Failed to delete VM disk: ($rm_disk_result.stderr)"}
    }

    let rm_meta_result = (do { bash -c $"rm -f ($meta_file)" } | complete)
    if $rm_meta_result.exit_code != 0 {
        return {success: false, error: $"Failed to delete metadata: ($rm_meta_result.stderr)"}
    }

    {
        success: true
        message: "Nested VM deleted"
    }
}

export def "container-create" [
    name: string                                 # Container name
    image: string                                # Container image
    parent_vm: string                            # Parent VM name
    --tag: string = "latest"                     # Image tag
    --cpu-millicores: int = 1000                 # CPU in millicores
    --memory-mb: int = 512                       # Memory in MB
    --networks: list<string> = []                # Networks
    --volumes: list<string> = []                 # Volume mounts
    --ports: list<string> = []                   # Port mappings
    --env: record = {}                           # Environment variables
    --auto-start = false                         # Auto-start
]: record {
    """
    Create a container inside a nested VM.

    Supports Docker, Podman, and containerd runtimes.
    """

    if ($name | is-empty) {
        return {success: false, error: "Container name required"}
    }

    if ($image | is-empty) {
        return {success: false, error: "Image required"}
    }

    let containers_dir = (get-containers-directory)
    bash -c $"mkdir -p ($containers_dir)" | complete

    let container_meta = {
        name: $name
        image: $"($image):($tag)"
        parent_vm: $parent_vm
        cpu_millicores: $cpu_millicores
        memory_mb: $memory_mb
        networks: $networks
        volumes: $volumes
        ports: $ports
        environment: $env
        auto_start: $auto_start
        created_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        status: "created"
    }

    # Save container metadata (no try-catch)
    let save_result = (do { bash -c $"cat > ($containers_dir)/($name).json << 'EOF'\n($container_meta | to json)\nEOF" } | complete)
    if $save_result.exit_code != 0 {
        return {success: false, error: $"Failed to save container metadata: ($save_result.stderr)"}
    }

    {
        success: true
        container: $name
        image: $container_meta.image
        parent_vm: $parent_vm
        cpu_millicores: $cpu_millicores
        memory_mb: $memory_mb
    }
}

export def "container-list" [
    parent_vm: string = ""                       # Filter by parent VM
]: table {
    """
    List containers, optionally filtered by parent VM.
    """

    let containers_dir = (get-containers-directory)

    if (not ($containers_dir | path exists)) {
        return []
    }

    bash -c $"ls -1 ($containers_dir)/*.json 2>/dev/null"
    | lines
    | each {|file|
        # Guard: Check if file can be opened and parsed as JSON (no try-catch)
        let json_result = (do { open $file | from json } | complete)
        if $json_result.exit_code == 0 {
            let meta = ($json_result.stdout)

            if ($parent_vm | is-empty) or ($meta.parent_vm == $parent_vm) {
                {
                    name: $meta.name
                    image: $meta.image
                    parent_vm: $meta.parent_vm
                    cpu_millicores: $meta.cpu_millicores
                    memory_mb: $meta.memory_mb
                    status: $meta.status
                    created: $meta.created_at
                }
            }
        } else {
            null
        }
    }
    | compact
}

export def "container-delete" [
    name: string                                 # Container name
]: record {
    """
    Delete a container.
    """

    let containers_dir = (get-containers-directory)
    let meta_file = $"($containers_dir)/($name).json"

    if (not ($meta_file | path exists)) {
        return {success: false, error: "Container not found"}
    }

    # Delete container metadata (no try-catch)
    let rm_result = (do { bash -c $"rm -f ($meta_file)" } | complete)
    if $rm_result.exit_code != 0 {
        return {success: false, error: $"Failed to delete container: ($rm_result.stderr)"}
    }

    {
        success: true
        message: "Container deleted"
    }
}

export def "nesting-stats" []: record {
    """
    Get nested provisioning statistics.
    """

    let nested_vms = (nested-vm-list)
    let containers = (container-list)

    let max_depth = (
        if ($nested_vms | length) > 0 {
            $nested_vms | get depth | math max
        } else {
            0
        }
    )

    {
        nested_vms: ($nested_vms | length)
        containers: ($containers | length)
        max_nesting_depth: $max_depth
        by_depth: (
            $nested_vms
            | group-by {|vm| $vm.depth}
            | each {|group| {depth: ($group | first | get depth), count: ($group | length)}}
        )
    }
}

def get-nested-directory []: string {
    """Get nested VMs directory"""
    "{{paths.workspace}}/vms/nested"
}

def get-containers-directory []: string {
    """Get containers directory"""
    "{{paths.workspace}}/vms/containers"
}

def get-nesting-depth [vm: string]: int {
    """Get nesting depth of VM"""
    let nested_dir = (get-nested-directory)
    let meta_file = $"($nested_dir)/($vm).json"

    if ($meta_file | path exists) {
        let meta = (open $meta_file | from json)
        $meta.nesting_depth
    } else {
        0
    }
}
