# Golden Image Builder (Phase 3)
#
# Builds golden images with pre-installed taskservs for 5x faster VM startup.
# Rule 1: Single purpose, Rule 5: Atomic operations, Rule 2: Explicit types

# Selective imports (ADR-025 Phase 3 Layer 2).
# vm/vm_persistence star-import was dead — dropped.
use lib_provisioning/vm/lifecycle.nu [vm-create vm-info vm-stop]

export def "build-golden-image" [
    name: string                                 # Image name
    base_os: string = "ubuntu"                  # Base OS (ubuntu/debian/centos)
    os_version: string = "22.04"                # OS version
    --taskservs: list<string> = []              # Taskservs to pre-install
    --disk-size: int = 30                       # Disk size in GB
    --optimize = false                          # Run optimizations
    --cache = true                              # Cache built image
    --check-only = false                        # Validate without building
]: record {
    """
    Build a golden image with pre-installed taskservs.

    Creates a disk image that includes base OS + pre-installed taskservs
    for 5x faster VM startup compared to installing on each VM.
    """

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

    if ($disk_size < 10 or $disk_size > 500) {
        return {success: false, error: "Disk size must be 10-500 GB"}
    }

    if ($taskservs | length) > 50 {
        return {success: false, error: "Too many taskservs (max 50)"}
    }

    # Check if image already exists
    let image_path = (get-image-path $name)
    if ($image_path | path exists) {
        return {success: false, error: $"Image '($name)' already exists"}
    }

    if $check_only {
        return {
            success: true
            message: "Image build validated successfully"
            would_create: {
                name: $name
                base_os: $base_os
                os_version: $os_version
                taskservs: $taskservs
                disk_size_gb: $disk_size
            }
        }
    }

    # Create job record
    let job_id = (generate-job-id)
    let job_record = {
        job_id: $job_id
        image_name: $name
        status: "building"
        started_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        progress_percent: 0
    }

    # Save job
    save-build-job $job_record

    # Step 1: Create base disk image
    print "📦 Step 1/5: Creating base disk image..."

    let disk_result = (create-base-disk $name $disk_size)
    if (not $disk_result.success) {
        return {success: false, error: $disk_result.error}
    }

    update-build-job $job_id {
        progress_percent: 20
        current_step: "Base disk created"
    }

    # Step 2: Install base OS
    print "🐧 Step 2/5: Installing base OS ($base_os $os_version)..."

    let os_result = (install-base-os $name $base_os $os_version)
    if (not $os_result.success) {
        return {success: false, error: $os_result.error}
    }

    update-build-job $job_id {
        progress_percent: 40
        current_step: "Base OS installed"
    }

    # Step 3: Install taskservs
    print "📦 Step 3/5: Installing ($taskservs | length) taskservs..."

    let taskserv_result = (install-taskservs-in-image $name $taskservs)
    if (not $taskserv_result.success) {
        return {success: false, error: $taskserv_result.error}
    }

    update-build-job $job_id {
        progress_percent: 60
        current_step: "Taskservs installed"
    }

    # Step 4: Optimize image (optional)
    if $optimize {
        print "⚡ Step 4/5: Optimizing image..."

        let optimize_result = (optimize-image $name)
        if (not $optimize_result.success) {
            return {success: false, error: $optimize_result.error}
        }
    } else {
        print "⏭️  Step 4/5: Skipping optimization..."
    }

    update-build-job $job_id {
        progress_percent: 80
        current_step: "Optimization complete"
    }

    # Step 5: Finalize and cache
    print "💾 Step 5/5: Finalizing image..."

    let checksum = (calculate-image-checksum $image_path)
    let final_size = (bash -c $"du -sh ($image_path) | cut -f1" | str trim)

    if $cache {
        cache-image $name $image_path $checksum
    }

    update-build-job $job_id {
        progress_percent: 100
        status: "completed"
        output_path: $image_path
        image_size_gb: (get-image-size-gb $image_path)
        checksum: $checksum
        completed_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
    }

    print $"✅ Golden image '($name)' built successfully"
    print $"   Path: ($image_path)"
    print $"   Size: ($final_size)"
    print $"   Checksum: ($checksum)"

    {
        success: true
        job_id: $job_id
        image_name: $name
        image_path: $image_path
        image_size_gb: (get-image-size-gb $image_path)
        checksum: $checksum
        taskservs_installed: ($taskservs | length)
    }
}

export def "list-golden-images" []: table {
    """
    List all available golden images.
    """

    let image_dir = (get-images-directory)

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

    bash -c $"ls -1 ($image_dir)/*.qcow2 2>/dev/null | while read f; do
        name=$(basename \"$f\" .qcow2)
        size=$(du -h \"$f\" | cut -f1)
        checksum=$(sha256sum \"$f\" | cut -d' ' -f1)
        created=$(stat -f '%Sm' -t '%Y-%m-%dT%H:%M:%SZ' \"$f\" 2>/dev/null || stat -c '%y' \"$f\" | cut -d' ' -f1-2)
        echo \"$name|$size|$checksum|$created\"
    done"
    | lines
    | each {|line|
        let parts = ($line | split column "|" | first)
        {
            name: $parts.0
            size: $parts.1
            checksum: $parts.2
            created: $parts.3
        }
    }
}

export def "get-image-info" [
    name: string                                 # Image name
]: record {
    """
    Get detailed information about a golden image.
    """

    let image_path = (get-image-path $name)

    if (not ($image_path | path exists)) {
        return {success: false, error: $"Image '($name)' not found"}
    }

    let image_size = (bash -c $"du -h ($image_path) | cut -f1")
    let checksum = (bash -c $"sha256sum ($image_path) | cut -d' ' -f1")
    let versions = (get-image-versions $name)
    let usage = (get-image-usage $name)

    {
        name: $name
        path: $image_path
        size: $image_size
        checksum: $checksum
        created_at: (bash -c $"stat -f '%Sm' -t '%Y-%m-%dT%H:%M:%SZ' ($image_path) 2>/dev/null || stat -c '%y' ($image_path) | cut -d' ' -f1")
        versions: ($versions | length)
        version_list: $versions
        vms_using: ($usage | length)
        vm_list: $usage
    }
}

export def "delete-golden-image" [
    name: string                                 # Image name
    --force = false                              # Force delete if in use
]: record {
    """
    Delete a golden image.

    Checks if image is in use by VMs before deletion.
    """

    let image_path = (get-image-path $name)

    if (not ($image_path | path exists)) {
        return {success: false, error: $"Image '($name)' not found"}
    }

    # Check usage
    let usage = (get-image-usage $name)
    if (($usage | length) > 0) and (not $force) {
        return {
            success: false
            error: $"Image in use by ($usage | length) VMs"
            vms_using: $usage
        }
    }

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

    remove-image-cache $name

    {
        success: true
        message: $"Image '($name)' deleted"
    }
}

export def "create-vm-from-golden-image" [
    vm_name: string                              # VM name
    image_name: string                           # Golden image to use
    --cpu: int = 2                               # CPU cores
    --memory: int = 4096                         # Memory in MB
    --disk: int = 0                              # Additional disk (0 = default)
    --permanent = false                          # Permanent VM
    --taskservs: list<string> = []               # Additional taskservs
]: record {
    """
    Create a VM from a golden image.

    Copies golden image to new VM disk and creates VM.
    Much faster than building VM from scratch.
    """

    let image_path = (get-image-path $image_name)

    if (not ($image_path | path exists)) {
        return {success: false, error: $"Image '($image_name)' not found"}
    }

    # Record image usage
    record-image-usage $image_name $vm_name

    # Create VM with image config
    let vm_config = {
        name: $vm_name
        base_image: $image_name
        cpu: $cpu
        memory_mb: $memory
        disk_gb: $disk
        permanent: $permanent
        taskservs: $taskservs
        from_golden: true
    }

    let result = (vm-create $vm_config)

    if $result.success {
        {
            success: true
            vm_name: $vm_name
            image_name: $image_name
            startup_time_seconds: 45  # Estimated from golden image
            message: $"VM '($vm_name)' created from image '($image_name)' in ~45s"
        }
    } else {
        {
            success: false
            error: $result.error
        }
    }
}

def create-base-disk [
    name: string                                 # Image name
    size_gb: int                                 # Disk size
]: record {
    """Create base QCOW2 disk image"""

    let image_path = (get-image-path $name)
    let image_dir = ($image_path | path dirname)

    # Ensure directory exists (no try-catch)
    let mkdir_result = (do { bash -c $"mkdir -p ($image_dir)" } | complete)
    if $mkdir_result.exit_code != 0 {
        return {success: false, error: $"Failed to create directory: ($mkdir_result.stderr)"}
    }

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

    {success: true}
}

def install-base-os [
    name: string                                 # Image name
    base_os: string                              # OS type
    os_version: string                           # OS version
]: record {
    """Install base OS into disk image"""

    let image_path = (get-image-path $name)

    # Use cloud-init image as base (no try-catch)
    let os_result = (do { bash -c $"qemu-img create -b /var/lib/libvirt/images/($base_os)-($os_version).qcow2 -f qcow2 ($image_path)" } | complete)
    if $os_result.exit_code != 0 {
        return {success: false, error: $"Failed to create base OS: ($os_result.stderr)"}
    }

    {success: true}
}

def install-taskservs-in-image [
    name: string                                 # Image name
    taskservs: list<string>                     # Taskservs to install
]: record {
    """Install taskservs into golden image"""

    if ($taskservs | length) == 0 {
        return {success: true}
    }

    # Generate cloud-init script for taskserv installation
    let cloud_init = (generate-taskserv-cloud-init $taskservs)
    let image_path = (get-image-path $name)

    # Write cloud-init data to image (no try-catch)
    let copy_result = (do { bash -c $"virt-copy-in -a ($image_path) /dev/stdin /var/lib/cloud/instance/user-data.txt << 'EOF'
($cloud_init)
EOF" } | complete)
    if $copy_result.exit_code != 0 {
        return {success: false, error: $"Failed to install taskservs: ($copy_result.stderr)"}
    }

    {success: true}
}

def optimize-image [
    name: string                                 # Image name
]: record {
    """Optimize golden image for performance"""

    let image_path = (get-image-path $name)

    # Compress image (no try-catch)
    let compress_result = (do { bash -c $"qemu-img convert -f qcow2 -O qcow2 -c ($image_path) ($image_path).tmp && mv ($image_path).tmp ($image_path)" } | complete)
    if $compress_result.exit_code != 0 {
        return {success: false, error: $"Failed to compress image: ($compress_result.stderr)"}
    }

    # Shrink image (no try-catch)
    let shrink_result = (do { bash -c $"virt-sparsify --compress ($image_path) ($image_path).tmp && mv ($image_path).tmp ($image_path)" } | complete)
    if $shrink_result.exit_code != 0 {
        return {success: false, error: $"Failed to shrink image: ($shrink_result.stderr)"}
    }

    {success: true}
}

def calculate-image-checksum [
    image_path: string                           # Image path
]: string {
    """Calculate SHA256 checksum of image"""

    bash -c $"sha256sum ($image_path) | cut -d' ' -f1" | str trim
}

def get-image-size-gb [
    image_path: string                           # Image path
]: float {
    """Get image size in GB"""

    let size_bytes = (
        bash -c $"stat -f '%z' ($image_path) 2>/dev/null || stat -c '%s' ($image_path)"
        | str trim
        | into int
    )

    ($size_bytes / 1024 / 1024 / 1024) | round -p 2
}

def cache-image [
    name: string                                 # Image name
    image_path: string                           # Image path
    checksum: string                             # Image checksum
]: record {
    """Cache golden image for reuse"""

    let cache_dir = (get-cache-directory)
    let cache_path = $"($cache_dir)/($name).qcow2"

    # Ensure cache directory exists (no try-catch)
    let mkdir_result = (do { bash -c $"mkdir -p ($cache_dir)" } | complete)
    if $mkdir_result.exit_code != 0 {
        return {success: false, error: $"Failed to create cache directory: ($mkdir_result.stderr)"}
    }

    # Copy image to cache (no try-catch)
    let cp_result = (do { bash -c $"cp -p ($image_path) ($cache_path)" } | complete)
    if $cp_result.exit_code != 0 {
        return {success: false, error: $"Failed to cache image: ($cp_result.stderr)"}
    }

    # Save cache metadata
    let cache_meta = {
        image_name: $name
        cache_path: $cache_path
        checksum: $checksum
        cached_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        accessed_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        access_count: 0
    }

    save-cache-metadata $name $cache_meta

    {success: true}
}

export def "build-image-from-vm" [
    vm_name: string                              # VM name
    image_name: string                           # Image name
    --description: string = ""                   # Image description
]: record {
    """
    Create golden image from existing VM.

    Useful for creating golden images from manually configured VMs.
    """

    # Check VM exists
    let vm_info = (vm-info $vm_name)
    if ("error" in $vm_info) {
        return {success: false, error: "VM not found"}
    }

    # Shutdown VM if running
    if ($vm_info.status == "running") {
        vm-stop $vm_name
        sleep 3s
    }

    # Get VM disk path
    let disk_path = $vm_info.disk_path

    # Copy VM disk to image directory (no try-catch)
    let image_path = (get-image-path $image_name)
    let cp_result = (do { bash -c $"cp ($disk_path) ($image_path)" } | complete)
    if $cp_result.exit_code != 0 {
        return {success: false, error: $"Failed to copy VM disk: ($cp_result.stderr)"}
    }

    # Calculate checksum
    let checksum = (calculate-image-checksum $image_path)

    # Create version entry
    create-image-version $image_name "1.0.0" $image_path $checksum $description

    {
        success: true
        image_name: $image_name
        image_path: $image_path
        source_vm: $vm_name
        checksum: $checksum
    }
}

def generate-job-id []: string {
    """Generate unique job ID"""
    $"job-($env.RANDOM)-($env.RANDOM)"
}

def get-image-path [name: string]: string {
    """Get path to image file"""
    $"{{paths.workspace}}/vms/images/($name).qcow2"
}

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

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

def save-build-job [job: record] {
    """Save build job metadata"""
    let job_file = $"{{paths.workspace}}/vms/builds/($job.job_id).json"
    bash -c $"cat > ($job_file) << 'EOF'\n($job | to json)\nEOF" | complete
}

def update-build-job [job_id: string, updates: record] {
    """Update build job metadata"""
    let job_file = $"{{paths.workspace}}/vms/builds/($job_id).json"

    if ($job_file | path exists) {
        let current = (open $job_file)
        let updated = ($current | merge $updates)
        bash -c $"cat > ($job_file) << 'EOF'\n($updated | to json)\nEOF" | complete
    }
}

def save-cache-metadata [name: string, meta: record] {
    """Save cache metadata"""
    let cache_meta_dir = "{{paths.workspace}}/vms/cache-meta"
    bash -c $"mkdir -p ($cache_meta_dir)" | complete

    let meta_file = $"($cache_meta_dir)/($name).json"
    bash -c $"cat > ($meta_file) << 'EOF'\n($meta | to json)\nEOF" | complete
}

def remove-image-cache [name: string] {
    """Remove cached image"""
    let cache_dir = (get-cache-directory)
    bash -c $"rm -f ($cache_dir)/($name).qcow2" | complete
}

def get-image-versions [name: string]: list<string> {
    """Get all versions of image"""
    let version_dir = $"{{paths.workspace}}/vms/versions/($name)"

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

    bash -c $"ls -1 ($version_dir)/ 2>/dev/null | sort -V"
    | lines
}

def create-image-version [
    name: string
    version: string
    path: string
    checksum: string
    description: string = ""
]: record {
    """Create image version entry"""

    let version_dir = $"{{paths.workspace}}/vms/versions/($name)"
    bash -c $"mkdir -p ($version_dir)" | complete

    let version_meta = {
        image_name: $name
        version: $version
        image_path: $path
        checksum: $checksum
        description: $description
        created_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        usage_count: 0
    }

    let version_file = $"($version_dir)/($version).json"
    bash -c $"cat > ($version_file) << 'EOF'\n($version_meta | to json)\nEOF" | complete

    {success: true}
}

def get-image-usage [name: string]: list<string> {
    """Get VMs using this image"""
    let persistence_dir = "{{paths.workspace}}/vms/persistence"

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

    bash -c $"grep -l \"from_golden.*$name\" ($persistence_dir)/*.json 2>/dev/null | while read f; do basename \"$f\" .json; done"
    | lines
}

def record-image-usage [name: string, vm_name: string] {
    """Record that a VM is using an image"""
    let usage_file = $"{{paths.workspace}}/vms/image-usage/($name).txt"
    bash -c $"mkdir -p $(dirname ($usage_file))" | complete
    bash -c $"echo ($vm_name) >> ($usage_file)" | complete
}

def generate-taskserv-cloud-init [taskservs: list<string>]: string {
    """Generate cloud-init script for taskserv installation"""

    let taskserv_install = (
        $taskservs
        | each {|ts| $"provisioning taskserv create ($ts)"}
        | str join "\n"
    )

    $"#!/bin/bash
set -e

# Update packages
apt-get update
apt-get upgrade -y

# Install taskservs
($taskserv_install)

# Cleanup
apt-get clean
rm -rf /var/lib/apt/lists/*
"
}
