#!/usr/bin/env nu

# Rustelo Application Deployment Script
# Nushell version of deploy.sh
# Handles deployment of the Rustelo application in various environments

def main [...args] {
    # Default configuration
    let mut config = {
        environment: "production",
        compose_file: "docker-compose.yml",
        build_args: "",
        migrate_db: false,
        backup_db: false,
        health_check: true,
        timeout: 300,
        project_name: "rustelo",
        docker_registry: "",
        image_tag: "latest",
        force_recreate: false,
        scale_replicas: 1,
        features: "production",
        use_default_features: false,
        debug: false
    }

    # Parse command line arguments
    let parsed = parse_deployment_args $args
    $config = ($config | merge $parsed.config)
    let command = $parsed.command

    print $"(ansi blue)🚀 Rustelo Deployment Manager(ansi reset)"

    # Validate command
    if ($command | is-empty) {
        print $"(ansi red)❌ No command specified(ansi reset)"
        show_deployment_usage
        exit 1
    }

    # Validate and set environment
    $config = (validate_deployment_environment $config)

    # Check prerequisites
    check_deployment_prerequisites $config

    # Set environment variables
    set_deployment_environment_vars $config

    # Execute command
    execute_deployment_command $command $config
}

# Parse deployment command line arguments
def parse_deployment_args [args] {
    let mut config = {}
    let mut command = ""
    let mut i = 0

    while $i < ($args | length) {
        let arg = ($args | get $i)

        match $arg {
            "-h" | "--help" => {
                show_deployment_usage
                exit 0
            }
            "-e" | "--env" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert environment ($args | get $i))
                }
            }
            "-f" | "--file" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert compose_file ($args | get $i))
                }
            }
            "-p" | "--project" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert project_name ($args | get $i))
                }
            }
            "-t" | "--tag" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert image_tag ($args | get $i))
                }
            }
            "-r" | "--registry" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert docker_registry ($args | get $i))
                }
            }
            "-s" | "--scale" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert scale_replicas ($args | get $i | into int))
                }
            }
            "--migrate" => {
                $config = ($config | upsert migrate_db true)
            }
            "--backup" => {
                $config = ($config | upsert backup_db true)
            }
            "--no-health-check" => {
                $config = ($config | upsert health_check false)
            }
            "--force-recreate" => {
                $config = ($config | upsert force_recreate true)
            }
            "--timeout" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert timeout ($args | get $i | into int))
                }
            }
            "--build-arg" => {
                $i = $i + 1
                if $i < ($args | length) {
                    let current_args = ($config | get -i build_args | default "")
                    $config = ($config | upsert build_args $"($current_args) --build-arg ($args | get $i)")
                }
            }
            "--features" => {
                $i = $i + 1
                if $i < ($args | length) {
                    $config = ($config | upsert features ($args | get $i))
                }
            }
            "--default-features" => {
                $config = ($config | upsert use_default_features true)
            }
            "--debug" => {
                $config = ($config | upsert debug true)
            }
            $cmd if not ($cmd | str starts-with "-") => {
                if ($command | is-empty) {
                    $command = $cmd
                }
            }
            _ => {
                print $"(ansi red)❌ Unknown option: ($arg)(ansi reset)"
                show_deployment_usage
                exit 1
            }
        }

        $i = $i + 1
    }

    {config: $config, command: $command}
}

# Show deployment usage information
def show_deployment_usage [] {
    print "Usage: nu deploy.nu [OPTIONS] COMMAND"
    print ""
    print "Commands:"
    print "    deploy          Deploy the application"
    print "    stop            Stop the application"
    print "    restart         Restart the application"
    print "    status          Show deployment status"
    print "    logs            Show application logs"
    print "    scale           Scale application replicas"
    print "    backup          Create database backup"
    print "    migrate         Run database migrations"
    print "    rollback        Rollback to previous version"
    print "    health          Check application health"
    print "    update          Update application to latest version"
    print "    clean           Clean up unused containers and images"
    print ""
    print "Options:"
    print "    -e, --env ENV              Environment (dev|staging|production) [default: production]"
    print "    -f, --file FILE            Docker compose file [default: docker-compose.yml]"
    print "    -p, --project PROJECT      Project name [default: rustelo]"
    print "    -t, --tag TAG              Docker image tag [default: latest]"
    print "    -r, --registry REGISTRY    Docker registry URL"
    print "    -s, --scale REPLICAS       Number of replicas [default: 1]"
    print "    --migrate                  Run database migrations before deployment"
    print "    --backup                   Create database backup before deployment"
    print "    --no-health-check          Skip health check after deployment"
    print "    --force-recreate           Force recreation of containers"
    print "    --timeout SECONDS          Deployment timeout [default: 300]"
    print "    --build-arg ARG            Docker build arguments"
    print "    --features FEATURES        Cargo features to enable [default: production]"
    print "    --default-features         Use default features instead of custom"
    print "    --debug                    Enable debug output"
    print "    -h, --help                 Show this help message"
    print ""
    print "Examples:"
    print "    nu deploy.nu deploy                                    # Deploy production"
    print "    nu deploy.nu deploy -e staging                         # Deploy staging"
    print "    nu deploy.nu deploy --migrate --backup                 # Deploy with migration and backup"
    print "    nu deploy.nu scale -s 3                               # Scale to 3 replicas"
    print "    nu deploy.nu logs                                     # Show logs"
    print "    nu deploy.nu health                                   # Check health status"
    print "    nu deploy.nu deploy --features \"auth,metrics\"         # Deploy with specific features"
    print "    nu deploy.nu deploy --default-features               # Deploy with all default features"
}

# Validate deployment environment
def validate_deployment_environment [config] {
    let environment = ($config | get environment)
    let mut updated_config = $config

    match $environment {
        "dev" | "development" => {
            $updated_config = ($updated_config | upsert environment "development")
            $updated_config = ($updated_config | upsert compose_file "docker-compose.yml")
        }
        "staging" => {
            $updated_config = ($updated_config | upsert environment "staging")
            $updated_config = ($updated_config | upsert compose_file "docker-compose.staging.yml")
        }
        "prod" | "production" => {
            $updated_config = ($updated_config | upsert environment "production")
            $updated_config = ($updated_config | upsert compose_file "docker-compose.yml")
        }
        _ => {
            print $"(ansi red)❌ Invalid environment: ($environment)(ansi reset)"
            print $"(ansi red)Valid environments: dev, staging, production(ansi reset)"
            exit 1
        }
    }

    $updated_config
}

# Check deployment prerequisites
def check_deployment_prerequisites [config] {
    print $"(ansi blue)🔍 Checking prerequisites...(ansi reset)"

    # Check if Docker is installed and running
    try {
        docker --version | ignore
    } catch {
        print $"(ansi red)❌ Docker is not installed or not in PATH(ansi reset)"
        exit 1
    }

    try {
        docker info | ignore
    } catch {
        print $"(ansi red)❌ Docker daemon is not running(ansi reset)"
        exit 1
    }

    # Check if Docker Compose is installed
    try {
        docker-compose --version | ignore
    } catch {
        print $"(ansi red)❌ Docker Compose is not installed or not in PATH(ansi reset)"
        exit 1
    }

    # Check if compose file exists
    let compose_file = ($config | get compose_file)
    if not ($compose_file | path exists) {
        print $"(ansi red)❌ Compose file not found: ($compose_file)(ansi reset)"
        exit 1
    }

    print $"(ansi green)✅ Prerequisites check passed(ansi reset)"
}

# Set deployment environment variables
def set_deployment_environment_vars [config] {
    $env.COMPOSE_PROJECT_NAME = ($config | get project_name)
    $env.DOCKER_REGISTRY = ($config | get docker_registry)
    $env.IMAGE_TAG = ($config | get image_tag)
    $env.ENVIRONMENT = ($config | get environment)

    # Source environment-specific variables
    let env_file = $".env.($config | get environment)"
    let default_env_file = ".env"

    if ($env_file | path exists) {
        print $"(ansi blue)📄 Loading environment variables from ($env_file)(ansi reset)"
        # Note: Nushell doesn't have direct source equivalent, would need custom implementation
    } else if ($default_env_file | path exists) {
        print $"(ansi blue)📄 Loading environment variables from ($default_env_file)(ansi reset)"
        # Note: Nushell doesn't have direct source equivalent, would need custom implementation
    }

    if ($config | get debug) {
        print $"(ansi blue)🐛 Environment variables set:(ansi reset)"
        print $"(ansi blue)  COMPOSE_PROJECT_NAME=($env.COMPOSE_PROJECT_NAME)(ansi reset)"
        print $"(ansi blue)  DOCKER_REGISTRY=($env.DOCKER_REGISTRY)(ansi reset)"
        print $"(ansi blue)  IMAGE_TAG=($env.IMAGE_TAG)(ansi reset)"
        print $"(ansi blue)  ENVIRONMENT=($env.ENVIRONMENT)(ansi reset)"
        print $"(ansi blue)  FEATURES=($config | get features)(ansi reset)"
        print $"(ansi blue)  USE_DEFAULT_FEATURES=($config | get use_default_features)(ansi reset)"
    }
}

# Execute deployment command
def execute_deployment_command [command, config] {
    match $command {
        "deploy" => {
            deployment_build_images $config
            deployment_create_backup $config
            deployment_run_migrations $config
            deployment_deploy_application $config
            deployment_wait_for_health $config
            deployment_show_status $config
        }
        "stop" => {
            deployment_stop_application $config
        }
        "restart" => {
            deployment_restart_application $config
            deployment_wait_for_health $config
        }
        "status" => {
            deployment_show_status $config
        }
        "logs" => {
            deployment_show_logs $config
        }
        "scale" => {
            deployment_scale_application $config
        }
        "backup" => {
            deployment_create_backup $config
        }
        "migrate" => {
            deployment_run_migrations $config
        }
        "rollback" => {
            deployment_rollback_application $config
        }
        "health" => {
            deployment_check_health $config
        }
        "update" => {
            deployment_update_application $config
            deployment_wait_for_health $config
        }
        "clean" => {
            deployment_cleanup $config
        }
        _ => {
            print $"(ansi red)❌ Unknown command: ($command)(ansi reset)"
            show_deployment_usage
            exit 1
        }
    }
}

# Build Docker images
def deployment_build_images [config] {
    print $"(ansi blue)🏗️  Building Docker images...(ansi reset)"

    let compose_file = ($config | get compose_file)
    let mut build_cmd = ["docker-compose", "-f", $compose_file, "build"]

    # Add build arguments
    let build_args = ($config | get build_args)
    if not ($build_args | is-empty) {
        $build_cmd = ($build_cmd | append ($build_args | split row " "))
    }

    # Add feature arguments
    if not ($config | get use_default_features) {
        $build_cmd = ($build_cmd | append ["--build-arg", $"CARGO_FEATURES=($config | get features)", "--build-arg", "NO_DEFAULT_FEATURES=true"])
    } else {
        $build_cmd = ($build_cmd | append ["--build-arg", "CARGO_FEATURES=", "--build-arg", "NO_DEFAULT_FEATURES=false"])
    }

    if ($config | get debug) {
        print $"(ansi blue)🐛 Build command: ($build_cmd | str join ' ')(ansi reset)"
    }

    try {
        run-external ($build_cmd | first) ..($build_cmd | skip 1)
        print $"(ansi green)✅ Docker images built successfully(ansi reset)"
    } catch {
        print $"(ansi red)❌ Failed to build Docker images(ansi reset)"
        exit 1
    }
}

# Create database backup
def deployment_create_backup [config] {
    if ($config | get backup_db) {
        print $"(ansi blue)💾 Creating database backup...(ansi reset)"

        let compose_file = ($config | get compose_file)
        let backup_file = $"backup_(date now | format date %Y%m%d_%H%M%S).sql"

        try {
            docker-compose -f $compose_file exec -T db pg_dump -U postgres rustelo_prod | save $backup_file
            print $"(ansi green)✅ Database backup created: ($backup_file)(ansi reset)"
        } catch {
            print $"(ansi red)❌ Failed to create database backup(ansi reset)"
            exit 1
        }
    }
}

# Run database migrations
def deployment_run_migrations [config] {
    if ($config | get migrate_db) {
        print $"(ansi blue)🚀 Running database migrations...(ansi reset)"

        let compose_file = ($config | get compose_file)

        try {
            docker-compose -f $compose_file run --rm migrate
            print $"(ansi green)✅ Database migrations completed successfully(ansi reset)"
        } catch {
            print $"(ansi red)❌ Database migrations failed(ansi reset)"
            exit 1
        }
    }
}

# Deploy application
def deployment_deploy_application [config] {
    print $"(ansi blue)🚀 Deploying application...(ansi reset)"

    let compose_file = ($config | get compose_file)
    let mut compose_cmd = ["docker-compose", "-f", $compose_file, "up", "-d"]

    if ($config | get force_recreate) {
        $compose_cmd = ($compose_cmd | append "--force-recreate")
    }

    let scale_replicas = ($config | get scale_replicas)
    if $scale_replicas > 1 {
        $compose_cmd = ($compose_cmd | append ["--scale", $"app=($scale_replicas)"])
    }

    if ($config | get debug) {
        print $"(ansi blue)🐛 Deploy command: ($compose_cmd | str join ' ')(ansi reset)"
    }

    try {
        run-external ($compose_cmd | first) ..($compose_cmd | skip 1)
        print $"(ansi green)✅ Application deployed successfully(ansi reset)"
    } catch {
        print $"(ansi red)❌ Failed to deploy application(ansi reset)"
        exit 1
    }
}

# Wait for application health
def deployment_wait_for_health [config] {
    if ($config | get health_check) {
        print $"(ansi blue)🏥 Waiting for application to be healthy...(ansi reset)"

        let start_time = (date now | into int)
        let health_url = "http://localhost:3030/health"
        let timeout = ($config | get timeout)

        loop {
            let current_time = (date now | into int)
            let elapsed = ($current_time - $start_time)

            if $elapsed > $timeout {
                print $"(ansi red)❌ Health check timeout after ($timeout) seconds(ansi reset)"
                exit 1
            }

            try {
                let response = (http get $health_url)
                if ($response | get -i status | default "" | str contains "healthy") {
                    print $"(ansi green)✅ Application is healthy(ansi reset)"
                    break
                }
            } catch {
                # Health check failed, continue retrying
            }

            if ($config | get debug) {
                print $"(ansi blue)🐛 Health check failed, retrying in 5 seconds... (($elapsed)s elapsed)(ansi reset)"
            }
            sleep 5sec
        }
    }
}

# Show deployment status
def deployment_show_status [config] {
    let compose_file = ($config | get compose_file)

    print $"(ansi blue)📊 Deployment status:(ansi reset)"
    try {
        docker-compose -f $compose_file ps
    } catch {
        print $"(ansi yellow)⚠️  Failed to get container status(ansi reset)"
    }

    print $"(ansi blue)📈 Container resource usage:(ansi reset)"
    try {
        docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
    } catch {
        print $"(ansi yellow)⚠️  Failed to get resource usage(ansi reset)"
    }
}

# Show application logs
def deployment_show_logs [config] {
    let compose_file = ($config | get compose_file)

    try {
        docker-compose -f $compose_file logs
    } catch {
        print $"(ansi red)❌ Failed to retrieve logs(ansi reset)"
        exit 1
    }
}

# Scale application
def deployment_scale_application [config] {
    let scale_replicas = ($config | get scale_replicas)
    let compose_file = ($config | get compose_file)

    print $"(ansi blue)📏 Scaling application to ($scale_replicas) replicas...(ansi reset)"

    try {
        docker-compose -f $compose_file up -d --scale $"app=($scale_replicas)"
        print $"(ansi green)✅ Application scaled successfully(ansi reset)"
    } catch {
        print $"(ansi red)❌ Failed to scale application(ansi reset)"
        exit 1
    }
}

# Stop application
def deployment_stop_application [config] {
    let compose_file = ($config | get compose_file)

    print $"(ansi blue)🛑 Stopping application...(ansi reset)"

    try {
        docker-compose -f $compose_file down
        print $"(ansi green)✅ Application stopped successfully(ansi reset)"
    } catch {
        print $"(ansi red)❌ Failed to stop application(ansi reset)"
        exit 1
    }
}

# Restart application
def deployment_restart_application [config] {
    let compose_file = ($config | get compose_file)

    print $"(ansi blue)🔄 Restarting application...(ansi reset)"

    try {
        docker-compose -f $compose_file restart
        print $"(ansi green)✅ Application restarted successfully(ansi reset)"
    } catch {
        print $"(ansi red)❌ Failed to restart application(ansi reset)"
        exit 1
    }
}

# Check application health
def deployment_check_health [config] {
    print $"(ansi blue)🏥 Checking application health...(ansi reset)"

    let health_url = "http://localhost:3030/health"

    try {
        let health_response = (http get $health_url)

        if ($health_response | get -i status | default "" | str contains "healthy") {
            print $"(ansi green)✅ Application is healthy(ansi reset)"
            print $"(ansi blue)📋 Health details:(ansi reset)"
            $health_response | table
        } else {
            print $"(ansi red)❌ Application is not healthy(ansi reset)"
            $health_response | table
            exit 1
        }
    } catch {
        print $"(ansi red)❌ Failed to check application health(ansi reset)"
        exit 1
    }
}

# Update application
def deployment_update_application [config] {
    let compose_file = ($config | get compose_file)

    print $"(ansi blue)🔄 Updating application...(ansi reset)"

    try {
        # Pull latest images
        docker-compose -f $compose_file pull

        # Restart with new images
        docker-compose -f $compose_file up -d --force-recreate

        print $"(ansi green)✅ Application updated successfully(ansi reset)"
    } catch {
        print $"(ansi red)❌ Failed to update application(ansi reset)"
        exit 1
    }
}

# Rollback application
def deployment_rollback_application [config] {
    print $"(ansi yellow)⚠️  Rollback functionality not implemented yet(ansi reset)"
    print $"(ansi yellow)💡 Please manually specify the desired image tag and redeploy(ansi reset)"
}

# Cleanup unused containers and images
def deployment_cleanup [config] {
    print $"(ansi blue)🧹 Cleaning up unused containers and images...(ansi reset)"

    try {
        # Remove stopped containers
        docker container prune -f

        # Remove unused images
        docker image prune -f

        # Remove unused volumes
        docker volume prune -f

        # Remove unused networks
        docker network prune -f

        print $"(ansi green)✅ Cleanup completed(ansi reset)"
    } catch {
        print $"(ansi red)❌ Cleanup failed(ansi reset)"
        exit 1
    }
}
