# VM SSH Connectivity and Provisioning
#
# SSH operations for VMs: connection, provisioning, file transfer.
# Rule 1: Single purpose, Rule 2: Explicit types

use ./backend_libvirt.nu *
use ./persistence.nu *

export def "vm-ssh" [
    vm_name: string               # VM name
    --command: string             # Command to execute (optional)
]: record {
    """
    SSH into VM or execute command.

    Handles VM IP discovery and SSH connectivity.

    Examples:
        # SSH shell
        provisioning vm ssh dev-rust

        # Execute command
        provisioning vm ssh dev-rust --command "uname -a"
    """

    # Get VM IP
    let ip = (get-vm-ip $vm_name)
    if ($ip | is-empty) {
        return {
            success: false
            error: $"Cannot determine IP for VM ($vm_name)"
        }
    }

    # Wait for SSH if needed
    let ssh_ready = (wait-for-ssh $ip)
    if not $ssh_ready {
        return {
            success: false
            error: $"SSH not responding on ($ip)"
        }
    }

    # Execute SSH
    if ($command | is-empty) {
        # Interactive shell
        bash -c $"ssh -o StrictHostKeyChecking=no root@($ip)"
        {success: true}
    } else {
        # Execute command (no try-catch)
        let output = (do { bash -c $"ssh -o StrictHostKeyChecking=no root@($ip) '($command)'" } | complete)
        {
            success: ($output.exit_code == 0)
            output: $output.stdout
            error: $output.stderr
        }
    }
}

export def "vm-scp-to" [
    vm_name: string               # VM name
    local_path: string            # Local file path
    remote_path: string           # Remote file path
]: record {
    """Copy file from host to VM"""

    let ip = (get-vm-ip $vm_name)
    if ($ip | is-empty) {
        return {success: false, error: $"Cannot determine IP for VM ($vm_name)"}
    }

    let wait = (wait-for-ssh $ip)
    if not $wait {
        return {success: false, error: $"SSH not ready on ($ip)"}
    }

    # Copy file via SCP (no try-catch)
    let result = (do { bash -c $"scp -r -o StrictHostKeyChecking=no ($local_path) root@($ip):($remote_path)" } | complete)

    {
        success: ($result.exit_code == 0)
        message: $"Copied ($local_path) to ($ip):($remote_path)"
        error: (if $result.exit_code != 0 { $result.stderr } else { "" })
    }
}

export def "vm-scp-from" [
    vm_name: string               # VM name
    remote_path: string           # Remote file path
    local_path: string            # Local file path
]: record {
    """Copy file from VM to host"""

    let ip = (get-vm-ip $vm_name)
    if ($ip | is-empty) {
        return {success: false, error: $"Cannot determine IP for VM ($vm_name)"}
    }

    let wait = (wait-for-ssh $ip)
    if not $wait {
        return {success: false, error: $"SSH not ready on ($ip)"}
    }

    # Copy file via SCP (no try-catch)
    let result = (do { bash -c $"scp -r -o StrictHostKeyChecking=no root@($ip):($remote_path) ($local_path)" } | complete)

    {
        success: ($result.exit_code == 0)
        message: $"Copied ($ip):($remote_path) to ($local_path)"
        error: (if $result.exit_code != 0 { $result.stderr } else { "" })
    }
}

export def "vm-exec" [
    vm_name: string               # VM name
    command: string               # Command to execute
]: record {
    """Execute command in VM and return output"""

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

    vm-ssh $vm_name --command=$command
}

def get-vm-ip [vm_name: string]: string {
    """Get VM IP address with caching"""

    # Try to get from libvirt first (fast)
    let libvirt_ip = (libvirt-get-vm-ip $vm_name)
    if ($libvirt_ip | length) > 0 {
        return $libvirt_ip
    }

    # Fallback: query state
    let state = (get-vm-state $vm_name)
    $state.ip_address // ""
}

def wait-for-ssh [ip: string, --timeout: int = 300]: bool {
    """Wait for SSH to become available"""

    let start_time = (date now)
    let timeout_duration = ($timeout)sec
    mut attempts = 0
    let max_attempts = ($timeout / 2) + 1  # Safety limit based on sleep 2sec

    while { $attempts < $max_attempts } {
        let elapsed = ((date now) - $start_time)

        if $elapsed >= $timeout_duration {
            return false
        }

        # Check SSH availability (no try-catch)
        let ssh_check = (do { bash -c $"ssh-keyscan -t rsa ($ip) 2>/dev/null" } | complete)

        if $ssh_check.exit_code == 0 {
            return true
        }

        # Wait before retry
        sleep 2sec
        $attempts += 1
    }

    false
}

export def "vm-provision" [
    vm_name: string               # VM name
    script: string                # Script to execute
]: record {
    """
    Provision VM with script.

    Uploads and executes provisioning script.
    """

    # Write script to temp file
    let temp_script = $"/tmp/provision-($vm_name)-($env.RANDOM).sh"

    # Create script file (no try-catch)
    let create_result = (do { bash -c $"cat > ($temp_script) << 'SCRIPT'\n($script)\nSCRIPT" } | complete)
    if $create_result.exit_code != 0 {
        return {success: false, error: $"Failed to create script: ($create_result.stderr)"}
    }

    # SCP script to VM
    let scp_result = (vm-scp-to $vm_name $temp_script "/tmp/provision.sh")
    if not $scp_result.success {
        bash -c $"rm -f ($temp_script)"
        return $scp_result
    }

    # Execute script
    let exec_result = (vm-ssh $vm_name --command "bash /tmp/provision.sh")

    # Cleanup
    bash -c $"rm -f ($temp_script)"
    vm-ssh $vm_name --command "rm -f /tmp/provision.sh" | ignore

    {
        success: $exec_result.success
        output: $exec_result.output
        error: $exec_result.error
    }
}
