#!/usr/bin/env nu

# Helper Functions for Provisioning Platform Deployment
#
# Provides common utilities for configuration management,
# validation, health checks, and rollback operations.

# Check deployment prerequisites
#
# Validates that all required tools and dependencies are available
# before attempting deployment.
#
# @returns: Validation result record
export def check-prerequisites []: nothing -> record {
    print "🔍 Checking prerequisites..."

    let checks = [
        {name: "nushell", cmd: "nu", min_version: "0.107.0"}
        {name: "docker", cmd: "docker", min_version: "20.10.0"}
        {name: "git", cmd: "git", min_version: "2.30.0"}
    ]

    mut failures = []

    for check in $checks {
        let available = (which $check.cmd | is-not-empty)

        if not $available {
            $failures = ($failures | append {
                tool: $check.name
                reason: "Not found in PATH"
            })
        }
    }

    if ($failures | is-empty) {
        print "✅ All prerequisites satisfied"
        {success: true, failures: []}
    } else {
        print "❌ Missing prerequisites:"
        for failure in $failures {
            print $"  - ($failure.tool): ($failure.reason)"
        }

        {
            success: false
            error: "Missing required tools"
            failures: $failures
        }
    }
}

# Validate deployment parameters
#
# @param platform: Target platform name
# @param mode: Deployment mode name
# @returns: Validation result record
export def validate-deployment-params [platform: string, mode: string]: nothing -> record {
    let valid_platforms = ["docker", "podman", "kubernetes", "orbstack"]
    let valid_modes = ["solo", "multi-user", "cicd", "enterprise"]

    if $platform not-in $valid_platforms {
        return {
            success: false
            error: $"Invalid platform '($platform)'. Must be one of: ($valid_platforms | str join ', ')"
        }
    }

    if $mode not-in $valid_modes {
        return {
            success: false
            error: $"Invalid mode '($mode)'. Must be one of: ($valid_modes | str join ', ')"
        }
    }

    {success: true}
}

# Build deployment configuration
#
# @param params: Configuration parameters record
# @returns: Complete deployment configuration
export def build-deployment-config [params: record]: nothing -> record {
    # Get default services for mode
    let default_services = get-default-services $params.mode

    # Merge with user-specified services if provided
    let services = if ($params.services | is-empty) {
        $default_services
    } else {
        # Filter to only user-specified services
        $default_services | where {|svc|
            $svc.name in $params.services or $svc.required
        }
    }

    {
        platform: $params.platform
        mode: $params.mode
        domain: $params.domain
        services: $services
        auto_generate_secrets: ($params.auto_generate_secrets? | default true)
    }
}

# Get default services for deployment mode
#
# @param mode: Deployment mode (solo, multi-user, cicd, enterprise)
# @returns: List of service configuration records
def get-default-services [mode: string]: nothing -> list<record> {
    let base_services = [
        {name: "orchestrator", description: "Task coordination", port: 8080, enabled: true, required: true}
        {name: "control-center", description: "Web UI", port: 8081, enabled: true, required: true}
        {name: "coredns", description: "DNS service", port: 5353, enabled: true, required: true}
    ]

    let mode_services = match $mode {
        "solo" => [
            {name: "oci-registry", description: "OCI Registry (Zot)", port: 5000, enabled: false, required: false}
            {name: "extension-registry", description: "Extension hosting", port: 8082, enabled: false, required: false}
            {name: "mcp-server", description: "Model Context Protocol", port: 8084, enabled: false, required: false}
            {name: "api-gateway", description: "REST API access", port: 8085, enabled: false, required: false}
        ]
        "multi-user" => [
            {name: "gitea", description: "Git server", port: 3000, enabled: true, required: true}
            {name: "postgres", description: "Shared database", port: 5432, enabled: true, required: true}
            {name: "oci-registry", description: "OCI Registry (Zot)", port: 5000, enabled: false, required: false}
        ]
        "cicd" => [
            {name: "gitea", description: "Git server", port: 3000, enabled: true, required: true}
            {name: "postgres", description: "Shared database", port: 5432, enabled: true, required: true}
            {name: "api-server", description: "REST API", port: 8083, enabled: true, required: true}
            {name: "oci-registry", description: "OCI Registry (Zot)", port: 5000, enabled: false, required: false}
        ]
        "enterprise" => [
            {name: "gitea", description: "Git server", port: 3000, enabled: true, required: true}
            {name: "postgres", description: "Shared database", port: 5432, enabled: true, required: true}
            {name: "api-server", description: "REST API", port: 8083, enabled: true, required: true}
            {name: "harbor", description: "Harbor OCI Registry", port: 5000, enabled: true, required: true}
            {name: "kms", description: "Cosmian KMS", port: 9998, enabled: true, required: true}
            {name: "prometheus", description: "Metrics", port: 9090, enabled: true, required: true}
            {name: "grafana", description: "Dashboards", port: 3001, enabled: true, required: true}
            {name: "loki", description: "Log aggregation", port: 3100, enabled: true, required: true}
            {name: "nginx", description: "Reverse proxy", port: 80, enabled: true, required: true}
        ]
        _ => []
    }

    $base_services | append $mode_services
}

# Save deployment configuration to TOML file
#
# @param config: Deployment configuration record
# @returns: Path to saved configuration file
export def save-deployment-config [config: record]: nothing -> path {
    let timestamp = (date now | format date "%Y%m%d_%H%M%S")
    let config_dir = $env.PWD | path join "configs"

    # Create configs directory if it doesn't exist
    mkdir $config_dir

    let config_file = $config_dir | path join $"deployment_($timestamp).toml"

    # Convert to TOML format
    let toml_content = $config | to toml

    $toml_content | save -f $config_file

    $config_file
}

# Load deployment configuration from TOML file
#
# @param config_path: Path to TOML configuration file
# @returns: Deployment configuration record
export def load-config-from-file [config_path: path]: nothing -> record {
    if not ($config_path | path exists) {
        error make {msg: $"Config file not found: ($config_path)"}
    }

    open $config_path | from toml
}

# Validate deployment configuration
#
# @param config: Deployment configuration record
# @param strict: Enable strict validation (default: false)
# @returns: Validation result record
export def validate-deployment-config [
    config: record
    --strict
]: nothing -> record {
    # Required fields
    let required_fields = ["platform", "mode", "domain", "services"]

    mut errors = []

    # Check required fields
    for field in $required_fields {
        if $field not-in ($config | columns) {
            $errors = ($errors | append $"Missing required field: ($field)")
        }
    }

    # Validate platform
    let valid_platforms = ["docker", "podman", "kubernetes", "orbstack"]
    if "platform" in ($config | columns) and ($config.platform not-in $valid_platforms) {
        $errors = ($errors | append $"Invalid platform: ($config.platform)")
    }

    # Validate mode
    let valid_modes = ["solo", "multi-user", "cicd", "enterprise"]
    if "mode" in ($config | columns) and ($config.mode not-in $valid_modes) {
        $errors = ($errors | append $"Invalid mode: ($config.mode)")
    }

    # Validate services
    if "services" in ($config | columns) {
        if ($config.services | is-empty) {
            $errors = ($errors | append "No services configured")
        }

        # In strict mode, validate required services
        if $strict {
            let required_services = $config.services | where required | get name
            let enabled_services = $config.services | where enabled | get name

            for req_svc in $required_services {
                if $req_svc not-in $enabled_services {
                    $errors = ($errors | append $"Required service not enabled: ($req_svc)")
                }
            }
        }
    }

    if ($errors | is-empty) {
        {success: true}
    } else {
        {
            success: false
            error: ($errors | str join "; ")
            errors: $errors
        }
    }
}

# Confirm deployment with user
#
# @param config: Deployment configuration record
# @returns: Boolean confirmation result
export def confirm-deployment [config: record]: nothing -> bool {
    print "
📋 Deployment Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"

    print $"Platform:  ($config.platform)"
    print $"Mode:      ($config.mode)"
    print $"Domain:    ($config.domain)"
    print ""
    print "Services:"

    for svc in $config.services {
        let status = if $svc.enabled { "✅" } else { "⬜" }
        let req_mark = if $svc.required { "(required)" } else { "" }
        print $"  ($status) ($svc.name):($svc.port) - ($svc.description) ($req_mark)"
    }

    print "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"

    let response = (input "Proceed with deployment? [y/N]: ")
    $response =~ "(?i)^y(es)?$"
}

# Check deployment health
#
# @param config: Deployment configuration record
# @returns: Health check result record
export def check-deployment-health [config: record]: nothing -> record {
    print "🏥 Running health checks..."

    let enabled_services = $config.services | where enabled

    let failed_services = ($enabled_services | each {|svc|
        let health_url = $"http://($config.domain):($svc.port)/health"
        print $"  Checking ($svc.name)..."

        let result = (http get $health_url --max-time 5sec | get status? | default "failed")

        if $result != "ok" {
            $svc.name
        } else {
            null
        }
    } | compact)

    if ($failed_services | is-empty) {
        print "✅ All health checks passed"
        {success: true}
    } else {
        print $"❌ Health checks failed for: ($failed_services | str join ', ')"
        {
            success: false
            error: $"Health checks failed for: ($failed_services | str join ', ')"
            failed_services: $failed_services
        }
    }
}

# Rollback deployment
#
# @param config: Deployment configuration record
# @returns: Rollback result record
export def rollback-deployment [config: record]: nothing -> record {
    print "🔄 Rolling back deployment..."

    match $config.platform {
        "docker" => { rollback-docker $config }
        "podman" => { rollback-podman $config }
        "kubernetes" => { rollback-kubernetes $config }
        "orbstack" => { rollback-orbstack $config }
        _ => {
            error make {msg: $"Unsupported platform for rollback: ($config.platform)"}
        }
    }
}

# Rollback Docker deployment
def rollback-docker [config: record]: nothing -> record {
    let compose_base = get-platform-path "docker-compose"
    let base_file = $compose_base | path join "docker-compose.yaml"

    let result = (do --ignore-errors { ^docker-compose -f $base_file down --volumes } | complete)
    if $result.exit_code == 0 {
        print "✅ Docker deployment rolled back successfully"
        {success: true, platform: "docker"}
    } else {
        {success: false, platform: "docker", error: $result.stderr}
    }
}

# Rollback Podman deployment
def rollback-podman [config: record]: nothing -> record {
    let compose_base = get-platform-path "docker-compose"
    let base_file = $compose_base | path join "docker-compose.yaml"

    let result = (do --ignore-errors { ^podman-compose -f $base_file down --volumes } | complete)
    if $result.exit_code == 0 {
        print "✅ Podman deployment rolled back successfully"
        {success: true, platform: "podman"}
    } else {
        {success: false, platform: "podman", error: $result.stderr}
    }
}

# Rollback Kubernetes deployment
def rollback-kubernetes [config: record]: nothing -> record {
    let namespace = "provisioning-platform"

    let result = (do --ignore-errors { ^kubectl delete namespace $namespace } | complete)
    if $result.exit_code == 0 {
        print "✅ Kubernetes deployment rolled back successfully"
        {success: true, platform: "kubernetes"}
    } else {
        {success: false, platform: "kubernetes", error: $result.stderr}
    }
}

# Rollback OrbStack deployment
def rollback-orbstack [config: record]: nothing -> record {
    # OrbStack uses Docker Compose
    rollback-docker $config | update platform "orbstack"
}

# Check platform availability
#
# @param platform: Platform name to check
# @returns: Platform availability record
export def check-platform-availability [platform: string]: nothing -> record {
    match $platform {
        "docker" => {
            let available = (which docker | is-not-empty)
            {platform: "docker", available: $available}
        }
        "podman" => {
            let available = (which podman | is-not-empty)
            {platform: "podman", available: $available}
        }
        "kubernetes" => {
            let available = (which kubectl | is-not-empty)
            {platform: "kubernetes", available: $available}
        }
        "orbstack" => {
            let available = (which orb | is-not-empty)
            {platform: "orbstack", available: $available}
        }
        _ => {
            {platform: $platform, available: false}
        }
    }
}

# Generate secrets for deployment
#
# @param config: Deployment configuration record
# @returns: Generated secrets record
export def generate-secrets [config: record]: nothing -> record {
    print "🔐 Generating secrets..."

    {
        jwt_secret: (random chars -l 64)
        postgres_password: (random chars -l 32)
        admin_password: (random chars -l 16)
        api_key: (random chars -l 48)
        encryption_key: (random chars -l 32)
    }
}

# Create deployment manifests
#
# @param config: Deployment configuration record
# @param secrets: Generated secrets record
# @returns: Path to manifests directory
export def create-deployment-manifests [config: record, secrets: record]: nothing -> path {
    let manifests_dir = $env.PWD | path join "manifests"
    mkdir $manifests_dir

    # Save secrets to file (in production, use proper secret management)
    let secrets_file = $manifests_dir | path join "secrets.toml"
    $secrets | to toml | save -f $secrets_file

    print $"📝 Secrets saved to: ($secrets_file)"

    $manifests_dir
}

# Get platform base path
#
# @param subpath: Optional subpath
# @returns: Full platform path
def get-platform-path [subpath: string = ""]: nothing -> path {
    let base_path = $env.PWD | path dirname | path dirname

    if $subpath == "" {
        $base_path
    } else {
        $base_path | path join $subpath
    }
}

# Get installer binary path
#
# @returns: Path to installer binary
export def get-installer-path []: nothing -> path {
    let installer_dir = $env.PWD | path dirname
    let installer_name = if $nu.os-info.name == "windows" {
        "provisioning-installer.exe"
    } else {
        "provisioning-installer"
    }

    # Check target/release first, then target/debug
    let release_path = $installer_dir | path join "target" "release" $installer_name
    let debug_path = $installer_dir | path join "target" "debug" $installer_name

    if ($release_path | path exists) {
        $release_path
    } else if ($debug_path | path exists) {
        $debug_path
    } else {
        error make {
            msg: "Installer binary not found"
            help: "Build with: cargo build --release"
        }
    }
}
