#!/usr/bin/env nu

# Deploy Provisioning Platform
# Complete platform deployment automation

def main [
    --mode: string = "solo"         # Deployment mode: solo, multi-user, cicd, enterprise
    --env-file: string = ".env"     # Environment file path
    --pull                          # Pull latest images before deploy
    --build                         # Build images from source
    --clean                         # Clean volumes before deploy (WARNING: data loss)
    --wait: int = 300               # Wait timeout for health checks (seconds)
    --skip-health-check             # Skip health checks after deploy
    --dry-run                       # Show what would be deployed without deploying
] {
    print $"(ansi green_bold)Provisioning Platform Deployment(ansi reset)"
    print $"Mode: ($mode)"
    print ""

    # Validate mode
    let valid_modes = ["solo", "multi-user", "cicd", "enterprise"]
    if $mode not-in $valid_modes {
        print $"(ansi red_bold)Error:(ansi reset) Invalid mode '($mode)'"
        print $"Valid modes: (valid_modes | str join ', ')"
        return 1
    }

    # Change to platform directory
    let platform_dir = $env.FILE_PWD | path dirname
    cd $platform_dir

    # Check prerequisites
    print $"(ansi cyan)Checking prerequisites...(ansi reset)"
    if not (check_docker) {
        return 1
    }
    if not (check_docker_compose) {
        return 1
    }

    # Generate .env if missing
    if not ($env_file | path exists) {
        if (".env.example" | path exists) {
            print $"(ansi yellow)Warning:(ansi reset) ($env_file) not found. Creating from .env.example"
            cp .env.example $env_file
            print $"(ansi yellow)Please edit ($env_file) and set required secrets before deploying!(ansi reset)"

            if not $dry_run {
                print ""
                print "Press Enter to continue after editing .env, or Ctrl+C to cancel..."
                input
            }
        } else {
            print $"(ansi red_bold)Error:(ansi reset) Neither ($env_file) nor .env.example found"
            return 1
        }
    }

    # Validate environment file
    print $"(ansi cyan)Validating environment configuration...(ansi reset)"
    if not (validate_env_file $env_file) {
        return 1
    }

    # Build compose file list
    let compose_files = build_compose_file_list $mode

    print $"(ansi cyan)Compose files:(ansi reset)"
    for file in $compose_files {
        print $"  - ($file)"
    }
    print ""

    if $dry_run {
        print $"(ansi yellow)DRY RUN: Would deploy with these settings(ansi reset)"
        return 0
    }

    # Clean volumes if requested
    if $clean {
        print $"(ansi yellow_bold)WARNING: --clean will delete all data volumes!(ansi reset)"
        print "Type 'yes' to confirm: "
        let confirm = input
        if $confirm != "yes" {
            print "Cancelled."
            return 0
        }

        print $"(ansi cyan)Cleaning volumes...(ansi reset)"
        docker_compose_down $compose_files --volumes
    }

    # Pull images if requested
    if $pull {
        print $"(ansi cyan)Pulling latest images...(ansi reset)"
        docker_compose_pull $compose_files
    }

    # Build images if requested
    if $build {
        print $"(ansi cyan)Building images from source...(ansi reset)"
        docker_compose_build $compose_files
    }

    # Create networks
    print $"(ansi cyan)Creating networks...(ansi reset)"
    create_networks

    # Deploy services
    print $"(ansi cyan)Deploying services...(ansi reset)"
    docker_compose_up $compose_files

    # Wait for health checks
    if not $skip_health_check {
        print ""
        print $"(ansi cyan)Waiting for services to be healthy (timeout: ($wait)s)...(ansi reset)"
        if not (wait_for_health $wait) {
            print $"(ansi red_bold)Error:(ansi reset) Some services failed health checks"
            print "Check logs with: docker compose logs"
            return 1
        }
    }

    # Display access URLs
    print ""
    print $"(ansi green_bold)✓ Platform deployed successfully!(ansi reset)"
    print ""
    display_access_urls $mode

    return 0
}

# Check if Docker is installed and running
def check_docker [] {
    try {
        docker ps | complete | get exit_code | $in == 0
        print $"(ansi green)✓ Docker is running(ansi reset)"
        true
    } catch {
        print $"(ansi red_bold)✗ Docker is not running or not installed(ansi reset)"
        print "Please install Docker and ensure it's running"
        false
    }
}

# Check if docker compose is installed
def check_docker_compose [] {
    try {
        docker compose version | complete | get exit_code | $in == 0
        print $"(ansi green)✓ docker compose is installed(ansi reset)"
        true
    } catch {
        print $"(ansi red_bold)✗ docker compose is not installed(ansi reset)"
        print "Please install docker compose plugin"
        false
    }
}

# Build list of compose files for mode
def build_compose_file_list [mode: string] {
    mut files = ["infrastructure/docker/docker-compose.yaml"]

    if $mode == "solo" {
        $files = ($files | append "infrastructure/docker/docker-compose.solo.yaml")
    } else if $mode == "multi-user" {
        $files = ($files | append "infrastructure/docker/docker-compose.multi-user.yaml")
    } else if $mode == "cicd" {
        $files = ($files | append "infrastructure/docker/docker-compose.multi-user.yaml")
        $files = ($files | append "infrastructure/docker/docker-compose.cicd.yaml")
    } else if $mode == "enterprise" {
        $files = ($files | append "infrastructure/docker/docker-compose.multi-user.yaml")
        $files = ($files | append "infrastructure/docker/docker-compose.cicd.yaml")
        $files = ($files | append "infrastructure/docker/docker-compose.enterprise.yaml")
    }

    $files
}

# Validate environment file
def validate_env_file [file: string] {
    let content = (open $file)

    # Check for placeholder secrets
    let placeholders = [
        "CHANGE_ME_RANDOM_SECRET_HERE",
        "CHANGE_ME_GITEA_SECRET_KEY",
        "CHANGE_ME_ADMIN_PASSWORD",
        "CHANGE_ME_POSTGRES_PASSWORD",
        "CHANGE_ME_API_SERVER_JWT_SECRET",
        "CHANGE_ME_GRAFANA_PASSWORD"
    ]

    mut has_placeholder = false
    for placeholder in $placeholders {
        if ($content | str contains $placeholder) {
            print $"(ansi yellow)Warning:(ansi reset) Found placeholder '($placeholder)' in ($file)"
            $has_placeholder = true
        }
    }

    if $has_placeholder {
        print $"(ansi yellow)Please replace all CHANGE_ME placeholders with actual secrets(ansi reset)"
        print "You can generate secrets with: openssl rand -base64 32"
    }

    true
}

# Create Docker networks
def create_networks [] {
    try {
        docker network create provisioning-net | ignore
    } catch {}

    try {
        docker network create provisioning-net-frontend | ignore
    } catch {}

    try {
        docker network create provisioning-net-backend | ignore
    } catch {}

    try {
        docker network create provisioning-net-storage | ignore
    } catch {}
}

# Run docker compose down
def docker_compose_down [files: list<string>, --volumes] {
    mut cmd = ["docker", "compose"]

    for file in $files {
        $cmd = ($cmd | append ["-f", $file])
    }

    $cmd = ($cmd | append "down")

    if $volumes {
        $cmd = ($cmd | append "--volumes")
    }

    run-external $cmd.0 ...($cmd | skip 1)
}

# Run docker compose pull
def docker_compose_pull [files: list<string>] {
    mut cmd = ["docker", "compose"]

    for file in $files {
        $cmd = ($cmd | append ["-f", $file])
    }

    $cmd = ($cmd | append "pull")

    run-external $cmd.0 ...($cmd | skip 1)
}

# Run docker compose build
def docker_compose_build [files: list<string>] {
    mut cmd = ["docker", "compose"]

    for file in $files {
        $cmd = ($cmd | append ["-f", $file])
    }

    $cmd = ($cmd | append "build")

    run-external $cmd.0 ...($cmd | skip 1)
}

# Run docker compose up
def docker_compose_up [files: list<string>] {
    mut cmd = ["docker", "compose"]

    for file in $files {
        $cmd = ($cmd | append ["-f", $file])
    }

    $cmd = ($cmd | append ["up", "-d"])

    run-external $cmd.0 ...($cmd | skip 1)
}

# Wait for services to be healthy
def wait_for_health [timeout: int] {
    let start = (date now)
    let end = ($start + ($timeout * 1sec))

    mut all_healthy = false

    while (date now) < $end {
        let containers = (docker ps --filter "name=provisioning-" --format "{{.Names}}")

        if ($containers | is-empty) {
            print $"(ansi red)No containers running(ansi reset)"
            sleep 2sec
            continue
        }

        mut healthy_count = 0
        mut total_count = 0

        for container in ($containers | lines) {
            $total_count = $total_count + 1

            let health = (docker inspect $container | from json | get 0.State.Health?.Status? | default "unknown")

            if $health == "healthy" or $health == "unknown" {
                $healthy_count = $healthy_count + 1
                print $"  (ansi green)✓(ansi reset) ($container): ($health)"
            } else {
                print $"  (ansi yellow)○(ansi reset) ($container): ($health)"
            }
        }

        if $healthy_count == $total_count {
            $all_healthy = true
            break
        }

        print $"Healthy: ($healthy_count)/($total_count) - Retrying in 5s..."
        sleep 5sec
        print ""
    }

    $all_healthy
}

# Display access URLs
def display_access_urls [mode: string] {
    print $"(ansi cyan_bold)Access URLs:(ansi reset)"
    print $"  Orchestrator:      http://localhost:8080"
    print $"  Control Center:    http://localhost:8081"

    if $mode in ["multi-user", "cicd", "enterprise"] {
        print $"  Gitea:             http://localhost:3000"
    }

    print $"  OCI Registry:      http://localhost:5000"
    print $"  Extension Registry: http://localhost:8082"

    if $mode in ["cicd", "enterprise"] {
        print $"  API Server:        http://localhost:8083"
    }

    if $mode == "enterprise" {
        print $"  Prometheus:        http://localhost:9090"
        print $"  Grafana:           http://localhost:3001"
        print $"  Kibana:            http://localhost:5601"
    }

    print ""
    print $"(ansi cyan_bold)Useful commands:(ansi reset)"
    print $"  View logs:         docker compose logs -f"
    print $"  Check status:      docker compose ps"
    print $"  Stop platform:     docker compose down"
    print $"  Restart service:   docker compose restart <service>"
}
