# Multi-Tier Deployment System (Phase 4)
#
# Deploy multi-tier applications with VMs and containers.
# Rule 1: Single purpose, Rule 5: Atomic operations

use ./network_management.nu *
use ./volume_management.nu *
use ./nested_provisioning.nu *
use ./lifecycle.nu *

export def "deployment-create" [
    name: string                                 # Deployment name
    --tiers: list<string>                        # Application tiers (web, app, db)
    --replicas: int = 1                          # Replicas per tier
    --strategy: string = "rolling"               # Deployment strategy
    --version: string = "1.0.0"                  # Version
]: record {
    """
    Create a multi-tier application deployment.

    Defines complete application stack with multiple tiers (web, app, db).
    """

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

    if ($tiers | length) == 0 {
        return {success: false, error: "At least one tier required"}
    }

    let deployment_dir = (get-deployments-directory)
    bash -c $"mkdir -p ($deployment_dir)" | complete

    # Create networks for deployment
    let networks = (create-deployment-networks $name $tiers)

    # Create deployment metadata
    let deployment = {
        name: $name
        version: $version
        tiers: $tiers
        replicas: $replicas
        strategy: $strategy
        networks: $networks
        created_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        status: "created"
        instances: []
    }

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

    {
        success: true
        deployment: $name
        version: $version
        tiers: $tiers
        replicas: $replicas
        networks: ($networks | length)
    }
}

export def "deployment-deploy" [
    name: string                                 # Deployment name
    --check = false                              # Dry-run
]: record {
    """
    Deploy a multi-tier application.

    Creates VMs and containers for all tiers.
    """

    let deployment_dir = (get-deployments-directory)
    let deployment_file = $"($deployment_dir)/($name).json"

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

    if $check {
        let meta = (open $deployment_file | from json)
        return {
            success: true
            would_deploy: {
                name: $meta.name
                version: $meta.version
                tiers: $meta.tiers
                replicas: $meta.replicas
                total_instances: ($meta.tiers | length) * $meta.replicas
            }
        }
    }

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

    let meta = ($meta_result.stdout)

    # Deploy each tier (no try-catch)
    let instances = (
        $meta.tiers
        | enumerate
        | each {|tier_info|
            let tier_num = $tier_info.index + 1
            let tier_name = $tier_info.item

            # Deploy replicas for this tier
            (0..$meta.replicas - 1)
            | each {|replica|
                let instance_name = $"($name)-($tier_name)-($replica + 1)"

                # Create instance
                let result = (
                    nested-vm-create $instance_name "host-vm" \
                        --cpu 2 \
                        --memory 2048 \
                        --disk 20 \
                        --networks [$"($name)-($tier_name)"] \
                        --auto-start
                )

                if $result.success {
                    {
                        tier: $tier_name
                        instance: $instance_name
                        status: "deployed"
                    }
                } else {
                    {
                        tier: $tier_name
                        instance: $instance_name
                        status: "failed"
                        error: $result.error
                    }
                }
            }
        }
        | flatten
    )

    # Update deployment with instances (no try-catch)
    let updated = (
        $meta
        | upsert status "deployed"
        | upsert instances $instances
        | upsert deployed_at (date now | format date "%Y-%m-%dT%H:%M:%SZ")
    )

    let update_result = (do { bash -c $"cat > ($deployment_file) << 'EOF'\n($updated | to json)\nEOF" } | complete)
    if $update_result.exit_code != 0 {
        return {success: false, error: $"Failed to update deployment: ($update_result.stderr)"}
    }

    {
        success: true
        deployment: $name
        instances_deployed: ($instances | length)
        instances: $instances
    }
}

export def "deployment-list" []: table {
    """
    List all deployments.
    """

    let deployment_dir = (get-deployments-directory)

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

    bash -c $"ls -1 ($deployment_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)
            {
                name: $meta.name
                version: $meta.version
                tiers: ($meta.tiers | length)
                status: $meta.status
                replicas: $meta.replicas
                total_instances: (($meta.instances // []) | length)
                created: $meta.created_at
            }
        } else {
            null
        }
    }
    | compact
}

export def "deployment-info" [
    name: string                                 # Deployment name
]: record {
    """
    Get detailed deployment information.
    """

    let deployment_dir = (get-deployments-directory)
    let deployment_file = $"($deployment_dir)/($name).json"

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

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

    let meta = ($meta_result.stdout)

    {
        success: true
        name: $meta.name
        version: $meta.version
        tiers: $meta.tiers
        replicas: $meta.replicas
        strategy: $meta.strategy
        status: $meta.status
        networks: ($meta.networks // [])
        instances: ($meta.instances // [])
        total_instances: (($meta.instances // []) | length)
        created: $meta.created_at
        deployed: ($meta.deployed_at // "not deployed")
    }
}

export def "deployment-delete" [
    name: string                                 # Deployment name
    --force = false                              # Force delete if running
]: record {
    """
    Delete a deployment.

    Removes all VMs, containers, networks, and storage.
    """

    let deployment_dir = (get-deployments-directory)
    let deployment_file = $"($deployment_dir)/($name).json"

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

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

    let meta = ($meta_result.stdout)

    # Delete instances (no try-catch)
    $meta.instances | each {|instance|
        nested-vm-delete $instance.instance --force=$force
    }

    # Delete networks (no try-catch)
    $meta.networks | each {|network|
        let del_result = (do { bash -c $"ip link delete ($network) 2>/dev/null || true" } | complete)
        if $del_result.exit_code != 0 {
            null  # Ignore network deletion errors
        }
    }

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

    {
        success: true
        message: "Deployment deleted"
        instances_deleted: ($meta.instances | length)
    }
}

export def "deployment-scale" [
    name: string                                 # Deployment name
    tier: string                                 # Tier to scale
    replicas: int                                # New replica count
]: record {
    """
    Scale a deployment tier.

    Increase or decrease number of replicas.
    """

    let deployment_dir = (get-deployments-directory)
    let deployment_file = $"($deployment_dir)/($name).json"

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

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

    let meta = ($meta_result.stdout)

    # Get current instances for this tier (no try-catch)
    let tier_instances = (
        $meta.instances
        | where {|i| ($i.tier == $tier)}
    )

    let current_count = ($tier_instances | length)

    if $replicas == $current_count {
        return {
            success: true
            message: "No scaling needed"
            tier: $tier
            current_replicas: $current_count
        }
    }

    if $replicas > $current_count {
        # Scale up (no try-catch)
        let new_replicas = $replicas - $current_count
        (0..$new_replicas - 1)
        | each {|i|
            let instance_name = $"($name)-($tier)-($current_count + $i + 1)"
            nested-vm-create $instance_name "host-vm" \
                --networks [$"($name)-($tier)"] \
                --auto-start
        }
    } else {
        # Scale down (no try-catch)
        let to_delete = ($tier_instances | last ($current_count - $replicas))
        $to_delete | each {|instance|
            nested-vm-delete $instance.instance
        }
    }

    {
        success: true
        tier: $tier
        previous_replicas: $current_count
        new_replicas: $replicas
        message: $"Scaled ($tier) to ($replicas) replicas"
    }
}

export def "deployment-health" [
    name: string                                 # Deployment name
]: record {
    """
    Check health of a deployment.

    Checks status of all instances and networks.
    """

    let deployment_dir = (get-deployments-directory)
    let deployment_file = $"($deployment_dir)/($name).json"

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

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

    let meta = ($meta_result.stdout)

    let instance_health = (
        $meta.instances
        | map {|instance|
            {
                instance: $instance.instance
                tier: $instance.tier
                status: $instance.status
            }
        }
    )

    let healthy = ($instance_health | where status == "deployed" | length)
    let unhealthy = ($instance_health | where status == "failed" | length)

    {
        success: true
        deployment: $name
        total_instances: ($instance_health | length)
        healthy: $healthy
        unhealthy: $unhealthy
        health_percent: (($healthy / ($instance_health | length) * 100) | math round -p 1)
        instances: $instance_health
    }
}

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

def create-deployment-networks [name: string, tiers: list<string>]: list<string> {
    """Create networks for deployment tiers"""

    # Create one network per tier
    $tiers
    | enumerate
    | map {|tier_info|
        let tier = $tier_info.item
        let network_name = $"($name)-($tier)"
        let subnet = $"192.168.($tier_info.index).0/24"

        let result = (
            network-create $network_name $subnet --type bridge
        )

        if $result.success {
            $network_name
        } else {
            null
        }
    }
    | compact
}
