# libvirt Backend Integration
#
# Low-level libvirt operations using virsh CLI.
# Rule 1: Single purpose, Rule 2: Explicit types, Rule 3: Early return
# Error handling: Result pattern (hybrid, no inline try-catch)

use lib_provisioning/result.nu *

export def "libvirt-create-vm" [
    config: record                 # VM configuration
]: record {
    """Create a virtual machine using libvirt"""

    # Rule 3: Early return for validation
    if ($config.name | is-empty) {
        return {
            success: false
            error: "VM name required"
            vm_id: null
        }
    }

    # Generate libvirt XML domain definition
    let xml = (generate-libvirt-xml $config)

    # Write XML to temporary file
    let temp_file = $"/tmp/vm-($config.name)-($env.RANDOM).xml"
    bash -c $"cat > ($temp_file) << 'EOF'\n($xml)\nEOF"

    # Define domain in libvirt using bash-check helper
    let define_result = (bash-check $"virsh define ($temp_file)")

    # Cleanup temp file (use bash-or for safe execution)
    bash -or $"rm -f ($temp_file)" null

    # Guard: Check define result
    if (is-err $define_result) {
        return {
            success: false
            error: $define_result.err
            vm_id: null
        }
    }

    # Get domain ID using bash-or with null fallback
    let domain_id = (bash-or $"virsh domid ($config.name) | tr -d '\n'" null)

    {
        success: true
        vm_id: $domain_id
        vm_name: $config.name
        message: $"VM ($config.name) defined successfully"
    }
}

def generate-libvirt-xml [config: record]: string {
    """Generate libvirt domain XML from VM config"""

    let memory_kb = ($config.memory_mb * 1024)
    let vcpus = $config.cpu

    $"<domain type='kvm'>
  <name>($config.name)</name>
  <memory unit='KiB'>($memory_kb)</memory>
  <currentMemory unit='KiB'>($memory_kb)</currentMemory>
  <vcpu placement='static'>($vcpus)</vcpu>
  <os>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
  </os>
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='(.get-vm-disk-path $config.name)'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <interface type='network'>
      <mac address='(.generate-mac-address)'/>
      <source network='default'/>
      <model type='rtl8139'/>
    </interface>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <memballoon model='virtio'/>
  </devices>
</domain>"
}

export def "libvirt-start-vm" [
    vm_name: string               # VM name
]: record {
    """Start a virtual machine"""

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

    # Execute using bash-check helper (no inline try-catch)
    let result = (bash-check $"virsh start ($vm_name)")

    # Guard: Check result
    if (is-err $result) {
        return {success: false, error: $result.err, vm_name: $vm_name}
    }

    {success: true, vm_name: $vm_name, message: $"VM ($vm_name) started"}
}

export def "libvirt-stop-vm" [
    vm_name: string               # VM name
    --force = false               # Force shutdown
]: record {
    """Stop a virtual machine"""

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

    # Guard: Build command based on flags
    let cmd = (if $force { $"virsh destroy ($vm_name)" } else { $"virsh shutdown ($vm_name)" })

    # Execute using bash-check helper (no inline try-catch)
    let result = (bash-check $cmd)

    # Guard: Check result
    if (is-err $result) {
        return {success: false, error: $result.err, vm_name: $vm_name}
    }

    {success: true, vm_name: $vm_name, message: $"VM ($vm_name) stopped"}
}

export def "libvirt-delete-vm" [
    vm_name: string               # VM name
]: record {
    """Delete a virtual machine and its disk"""

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

    # Guard: Check if running using bash-or helper (no inline try-catch)
    let is_running = (
        (bash-or $"virsh domstate ($vm_name) | grep -q running; echo $?" "1") | str trim == "0"
    )

    # Stop VM if running
    if $is_running {
        let stop_result = (libvirt-stop-vm $vm_name --force)
        if not $stop_result.success {
            return $stop_result
        }
    }

    # Undefine domain using bash-check helper
    let undefine_result = (bash-check $"virsh undefine ($vm_name)")

    # Guard: Check undefine result
    if (is-err $undefine_result) {
        return {success: false, error: $undefine_result.err, vm_name: $vm_name}
    }

    # Delete disk using bash-or helper (safe, ignores errors)
    let disk_path = (get-vm-disk-path $vm_name)
    bash -or $"rm -f ($disk_path)" null

    {success: true, vm_name: $vm_name, message: $"VM ($vm_name) deleted"}
}

export def "libvirt-list-vms" []: table {
    """List all libvirt VMs"""

    # Guard: List VMs using bash-wrap helper
    let list_result = (bash-wrap "virsh list --all --name")

    # Guard: Check if listing succeeded
    if (is-err $list_result) {
        return []  # Return empty list on error
    }

    # Process VM list
    $list_result.ok
    | lines
    | where {|x| ($x | length) > 0}
    | each {|vm_name|
        # Get state using bash-or helper with fallback
        let state = (bash-or $"virsh domstate ($vm_name) | tr -d '\n'" "unknown")

        {
            name: $vm_name
            state: $state
            backend: "libvirt"
        }
    }
}

export def "libvirt-get-vm-info" [
    vm_name: string               # VM name
]: record {
    """Get detailed VM information from libvirt"""

    # Guard: Input validation
    if ($vm_name | is-empty) {
        return {error: "VM name required"}
    }

    # Get state using bash-or helper
    let state = (bash-or $"virsh domstate ($vm_name) | tr -d '\n'" "unknown")

    # Get domain ID using bash-or helper
    let domain_id = (bash-or $"virsh domid ($vm_name) | tr -d '\n'" null)

    # Get detailed info using bash-wrap helper
    let info = (
        (bash-wrap $"virsh dominfo ($vm_name)")
        | match-result
            {|output|
                $output | lines
                | reduce fold {|line, acc|
                    let parts = ($line | split row " " | where {|x| ($x | length) > 0})
                    if ($parts | length) >= 2 {
                        let key = ($parts | get 0)
                        let value = ($parts | skip 1 | str join " ")
                        {($key): $value} | merge $acc
                    } else {
                        $acc
                    }
                } {}
            }
            {|_err| {}}  # Return empty record on error
    )

    {
        name: $vm_name
        state: $state
        id: $domain_id
        cpu_cores: (($info | get "CPU(s)" 2>/dev/null) // "unknown")
        memory_mb: (($info | get "Max memory" 2>/dev/null) // "unknown")
    }
}

export def "libvirt-get-vm-ip" [
    vm_name: string               # VM name
]: string {
    """Get VM IP address from libvirt"""

    # Guard: Input validation
    if ($vm_name | is-empty) {
        return ""
    }

    # Get IP using bash-wrap helper
    (bash-wrap $"virsh domifaddr ($vm_name)")
    | match-result
        {|output|
            $output
            | lines
            | skip 2  # Skip header
            | where {|x| ($x | length) > 0}
            | get 0?  # Optional access
            | split row " "
            | where {|x| ($x | length) > 0}
            | get 2?  # Optional access
            | split row "/"
            | get 0
        }
        {|_err| ""}  # Return empty string on error
}

def get-vm-disk-path [vm_name: string]: string {
    """Get disk path for VM"""
    $"{{paths.workspace}}/vms/permanent/($vm_name)/disk.qcow2"
}

def generate-mac-address []: string {
    """Generate random MAC address"""
    let random = (bash -c "echo $((RANDOM % 256))").0
    $"52:54:00:($random | fill -a r -w 2 -c 0):($random | fill -a r -w 2 -c 0):($random | fill -a r -w 2 -c 0)"
}

export def "libvirt-create-disk" [
    vm_name: string               # VM name
    size_gb: int                  # Disk size in GB
]: record {
    """Create QCOW2 disk for VM"""

    # Guard: Input validation
    if ($vm_name | is-empty) {
        return {success: false, error: "VM name required", path: null}
    }
    if $size_gb <= 0 {
        return {success: false, error: "Size must be positive", path: null}
    }

    let disk_path = (get-vm-disk-path $vm_name)
    let disk_dir = ($disk_path | path dirname)

    # Create directory (safe to ignore errors)
    bash -or $"mkdir -p ($disk_dir)" null

    # Create QCOW2 disk using bash-check helper
    let result = (bash-check $"qemu-img create -f qcow2 ($disk_path) ($size_gb)G")

    # Guard: Check result
    if (is-err $result) {
        return {success: false, error: $result.err, path: null}
    }

    {success: true, path: $disk_path, size_gb: $size_gb, format: "qcow2"}
}
