#!/usr/bin/env nu

# Container build tool - builds Docker containers for platform services
#
# Builds:
# - Orchestrator service container
# - Control center container
# - Web UI container
# - All-in-one development container
# - Platform-specific containers

use std log

def main [
    --dist-dir: string = "dist"
    --output-registry: string = "local"
    --tag-prefix: string = "provisioning"
    --version: string = ""
    --build-args: string = ""
    --platforms: string = "linux/amd64"
    --push
    --cache
    --verbose
    --parallel
] {

    let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
    let dist_root = ($dist_dir | path expand)

    # Detect version if not provided
    let detected_version = if $version == "" {
        detect_version $repo_root
    } else {
        $version
    }

    let container_config = {
        dist_dir: $dist_root
        output_registry: $output_registry
        tag_prefix: $tag_prefix
        version: $detected_version
        build_args: ($build_args | if $in == "" { [] } else { $in | split row "," | each { str trim } })
        platforms: ($platforms | split row "," | each { str trim })
        push: $push
        cache: $cache
        verbose: $verbose
        parallel: $parallel
        repo_root: $repo_root
    }

    log info $"Starting container builds with config: ($container_config)"

    # Validate distribution directory
    if not ($dist_root | path exists) {
        log error $"Distribution directory does not exist: ($dist_root)"
        exit 1
    }

    # Check Docker availability
    let docker_available = check_docker_availability

    if not $docker_available {
        log error "Docker is not available or not running"
        exit 1
    }

    # Define container configurations
    let container_definitions = [
        {
            name: "orchestrator"
            dockerfile: "Dockerfile.orchestrator"
            context: "."
            binary_path: ($dist_root | path join "platform")
            dependencies: ["orchestrator"]
        },
        {
            name: "control-center"
            dockerfile: "Dockerfile.control-center"
            context: "."
            binary_path: ($dist_root | path join "platform")
            dependencies: ["control-center"]
        },
        {
            name: "web-ui"
            dockerfile: "Dockerfile.web-ui"
            context: "."
            binary_path: ($dist_root | path join "platform")
            dependencies: ["control-center-ui"]
        },
        {
            name: "all-in-one"
            dockerfile: "Dockerfile.all-in-one"
            context: "."
            binary_path: ($dist_root | path join "platform")
            dependencies: ["orchestrator", "control-center", "control-center-ui"]
        }
    ]

    # Create Dockerfiles if they don't exist
    ensure_dockerfiles_exist $container_definitions $container_config

    # Build containers
    let build_results = if $container_config.parallel {
        build_containers_parallel $container_definitions $container_config
    } else {
        build_containers_sequential $container_definitions $container_config
    }

    # Push containers if requested
    let push_results = if $container_config.push {
        push_containers $build_results $container_config
    } else {
        { status: "skipped", pushed: [] }
    }

    let summary = {
        total_containers: ($container_definitions | length)
        successful_builds: ($build_results | where status == "success" | length)
        failed_builds: ($build_results | where status == "failed" | length)
        push_results: $push_results
        container_config: $container_config
        build_results: $build_results
    }

    if $summary.failed_builds > 0 {
        log error $"Container build completed with ($summary.failed_builds) failures"
        exit 1
    } else {
        log info $"Container build completed successfully - ($summary.successful_builds) containers built"
    }

    return $summary
}

# Detect version from git or other sources
def detect_version [repo_root: string]
{
    try {
        cd $repo_root
        let git_version = (git describe --tags --always --dirty 2>/dev/null | complete)
        if $git_version.exit_code == 0 and ($git_version.stdout | str trim) != "" {
            return ($git_version.stdout | str trim)
        }
        return $"dev-(date now | format date "%Y%m%d")"
    } catch {
        return "dev-unknown"
    }
}

# Check if Docker is available
def check_docker_availability []
{
    try {
        let docker_check = (docker --version | complete)
        return ($docker_check.exit_code == 0)
    } catch {
        return false
    }
}

# Ensure Dockerfiles exist, create them if they don't
def ensure_dockerfiles_exist [
    container_definitions: list
    container_config: record
] {
    let dockerfile_dir = ($container_config.repo_root | path join "docker")

    # Ensure docker directory exists
    mkdir $dockerfile_dir

    for container in $container_definitions {
        let dockerfile_path = ($dockerfile_dir | path join $container.dockerfile)

        if not ($dockerfile_path | path exists) {
            log info $"Creating Dockerfile for ($container.name): ($dockerfile_path)"
            create_dockerfile $container $container_config $dockerfile_path
        }
    }
}

# Create a Dockerfile for a container
def create_dockerfile [
    container: record
    container_config: record
    dockerfile_path: string
] {
    let dockerfile_content = match $container.name {
        "orchestrator" => { create_orchestrator_dockerfile $container_config }
        "control-center" => { create_control_center_dockerfile $container_config }
        "web-ui" => { create_web_ui_dockerfile $container_config }
        "all-in-one" => { create_all_in_one_dockerfile $container_config }
        _ => { create_generic_dockerfile $container $container_config }
    }

    $dockerfile_content | save $dockerfile_path
    log info $"Created Dockerfile: ($dockerfile_path)"
}

# Create orchestrator Dockerfile
def create_orchestrator_dockerfile [config: record]
{
    $"# Provisioning Orchestrator Container
# Version: ($config.version)

FROM debian:bookworm-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y \\
    ca-certificates \\
    curl \\
    && rm -rf /var/lib/apt/lists/*

# Create application user
RUN useradd -r -s /bin/false -m -d /app provisioning

# Copy binary
COPY dist/platform/orchestrator-* /usr/local/bin/provisioning-orchestrator
RUN chmod +x /usr/local/bin/provisioning-orchestrator

# Copy core libraries
COPY dist/core /app/core
COPY dist/config /app/config

# Set ownership
RUN chown -R provisioning:provisioning /app

# Switch to application user
USER provisioning
WORKDIR /app

# Expose ports
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
    CMD curl -f http://localhost:8080/health || exit 1

# Run orchestrator
CMD [\"/usr/local/bin/provisioning-orchestrator\", \"--host\", \"0.0.0.0\", \"--port\", \"8080\"]
"
}

# Create control center Dockerfile
def create_control_center_dockerfile [config: record]
{
    $"# Provisioning Control Center Container
# Version: ($config.version)

FROM debian:bookworm-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y \\
    ca-certificates \\
    curl \\
    && rm -rf /var/lib/apt/lists/*

# Create application user
RUN useradd -r -s /bin/false -m -d /app provisioning

# Copy binary
COPY dist/platform/control-center-* /usr/local/bin/control-center
RUN chmod +x /usr/local/bin/control-center

# Copy core libraries
COPY dist/core /app/core
COPY dist/config /app/config

# Set ownership
RUN chown -R provisioning:provisioning /app

# Switch to application user
USER provisioning
WORKDIR /app

# Expose ports
EXPOSE 9080

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
    CMD curl -f http://localhost:9080/health || exit 1

# Run control center
CMD [\"/usr/local/bin/control-center\", \"--host\", \"0.0.0.0\", \"--port\", \"9080\"]
"
}

# Create web UI Dockerfile
def create_web_ui_dockerfile [config: record]
{
    $"# Provisioning Web UI Container
# Version: ($config.version)

FROM nginx:alpine

# Copy built UI assets
COPY dist/platform/control-center-ui /usr/share/nginx/html

# Copy nginx configuration
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf

# Expose port
EXPOSE 80

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
    CMD curl -f http://localhost/health || exit 1

# Run nginx
CMD [\"nginx\", \"-g\", \"daemon off;\"]
"
}

# Create all-in-one Dockerfile
def create_all_in_one_dockerfile [config: record]
{
    $"# Provisioning All-in-One Container
# Version: ($config.version)

FROM debian:bookworm-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y \\
    ca-certificates \\
    curl \\
    nginx \\
    supervisor \\
    && rm -rf /var/lib/apt/lists/*

# Create application user
RUN useradd -r -s /bin/false -m -d /app provisioning

# Copy binaries
COPY dist/platform/orchestrator-* /usr/local/bin/provisioning-orchestrator
COPY dist/platform/control-center-* /usr/local/bin/control-center
RUN chmod +x /usr/local/bin/provisioning-orchestrator /usr/local/bin/control-center

# Copy core libraries and configuration
COPY dist/core /app/core
COPY dist/config /app/config

# Copy web UI
COPY dist/platform/control-center-ui /usr/share/nginx/html

# Copy supervisor configuration
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY docker/nginx-all-in-one.conf /etc/nginx/sites-available/default

# Set ownership
RUN chown -R provisioning:provisioning /app

# Expose ports
EXPOSE 80 8080 9080

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
    CMD curl -f http://localhost/health && curl -f http://localhost:8080/health || exit 1

# Run supervisor
CMD [\"/usr/bin/supervisord\", \"-c\", \"/etc/supervisor/conf.d/supervisord.conf\"]
"
}

# Create generic Dockerfile
def create_generic_dockerfile [container: record, config: record]
{
    $"# Generic Provisioning Service Container
# Service: ($container.name)
# Version: ($config.version)

FROM debian:bookworm-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y \\
    ca-certificates \\
    curl \\
    && rm -rf /var/lib/apt/lists/*

# Create application user
RUN useradd -r -s /bin/false -m -d /app provisioning

# Copy binaries
COPY dist/platform/* /usr/local/bin/
RUN chmod +x /usr/local/bin/*

# Copy core libraries
COPY dist/core /app/core
COPY dist/config /app/config

# Set ownership
RUN chown -R provisioning:provisioning /app

# Switch to application user
USER provisioning
WORKDIR /app

# Expose default port
EXPOSE 8080

# Run service
CMD [\"sh\", \"-c\", \"echo 'Container for ($container.name) - configure as needed'\"]
"
}

# Build containers sequentially
def build_containers_sequential [
    container_definitions: list
    container_config: record
]
{
    $container_definitions | each {|container|
        build_single_container $container $container_config
    }
}

# Build containers in parallel
def build_containers_parallel [
    container_definitions: list
    container_config: record
]
{
    # For simplicity, using sequential for now
    # In a real implementation, you might use background processes
    build_containers_sequential $container_definitions $container_config
}

# Build a single container
def build_single_container [
    container: record
    container_config: record
]
{
    log info $"Building container: ($container.name)"

    let start_time = (date now)
    let dockerfile_path = ($container_config.repo_root | path join "docker" $container.dockerfile)
    let image_tag = $"($container_config.tag_prefix)/($container.name):($container_config.version)"

    try {
        # Check if required binaries exist
        let missing_deps = check_container_dependencies $container $container_config

        if ($missing_deps | length) > 0 {
            return {
                container: $container.name
                status: "failed"
                reason: $"Missing dependencies: ($missing_deps | str join ', ')"
                duration: ((date now) - $start_time)
            }
        }

        # Build Docker command
        let docker_cmd = ["docker", "build"]

        # Add build arguments
        for arg in $container_config.build_args {
            $docker_cmd = ($docker_cmd | append ["--build-arg", $arg])
        }

        # Add cache options
        if not $container_config.cache {
            $docker_cmd = ($docker_cmd | append "--no-cache")
        }

        # Add platform support for multi-arch
        if ($container_config.platforms | length) > 1 {
            $docker_cmd = ($docker_cmd | append ["--platform", ($container_config.platforms | str join ",")])
        } else {
            $docker_cmd = ($docker_cmd | append ["--platform", ($container_config.platforms | get 0)])
        }

        # Add tags
        $docker_cmd = ($docker_cmd | append ["-t", $image_tag])

        # Add dockerfile and context
        $docker_cmd = ($docker_cmd | append ["-f", $dockerfile_path, $container.context])

        # Execute build
        cd ($container_config.repo_root)

        if $container_config.verbose {
            log info $"Running: ($docker_cmd | str join ' ')"
        }

        let build_result = (run-external --redirect-combine $docker_cmd.0 ...$docker_cmd.1.. | complete)

        if $build_result.exit_code == 0 {
            # Get image size
            let image_info = get_image_info $image_tag

            log info $"Successfully built container: ($container.name) -> ($image_tag)"

            {
                container: $container.name
                status: "success"
                image_tag: $image_tag
                image_size: $image_info.size
                duration: ((date now) - $start_time)
            }
        } else {
            log error $"Failed to build container ($container.name): ($build_result.stderr)"
            {
                container: $container.name
                status: "failed"
                reason: $build_result.stderr
                duration: ((date now) - $start_time)
            }
        }

    } catch {|err|
        log error $"Failed to build container ($container.name): ($err.msg)"
        {
            container: $container.name
            status: "failed"
            reason: $err.msg
            duration: ((date now) - $start_time)
        }
    }
}

# Check container dependencies
def check_container_dependencies [
    container: record
    container_config: record
]
{
    let missing_deps = []

    for dep in $container.dependencies {
        let binary_pattern = $"($dep)*"
        let found_binaries = (find $container.binary_path -name $binary_pattern -type f)

        if ($found_binaries | length) == 0 {
            $missing_deps = ($missing_deps | append $dep)
        }
    }

    return $missing_deps
}

# Get image information
def get_image_info [image_tag: string]
{
    try {
        let inspect_result = (docker inspect $image_tag | from json | get 0)
        {
            size: $inspect_result.Size
            created: $inspect_result.Created
            architecture: $inspect_result.Architecture
        }
    } catch {
        { size: 0, created: "", architecture: "unknown" }
    }
}

# Push containers to registry
def push_containers [
    build_results: list
    container_config: record
]
{
    log info $"Pushing containers to registry: ($container_config.output_registry)"

    let successful_builds = ($build_results | where status == "success")
    let push_results = []

    for build in $successful_builds {
        try {
            log info $"Pushing: ($build.image_tag)"
            let push_result = (docker push $build.image_tag | complete)

            if $push_result.exit_code == 0 {
                log info $"Successfully pushed: ($build.image_tag)"
                $push_results = ($push_results | append {
                    image: $build.image_tag
                    status: "success"
                })
            } else {
                log error $"Failed to push ($build.image_tag): ($push_result.stderr)"
                $push_results = ($push_results | append {
                    image: $build.image_tag
                    status: "failed"
                    reason: $push_result.stderr
                })
            }

        } catch {|err|
            log error $"Failed to push ($build.image_tag): ($err.msg)"
            $push_results = ($push_results | append {
                image: $build.image_tag
                status: "failed"
                reason: $err.msg
            })
        }
    }

    {
        status: (if ($push_results | where status == "failed" | length) > 0 { "partial" } else { "success" })
        total_pushed: ($push_results | where status == "success" | length)
        failed_pushes: ($push_results | where status == "failed" | length)
        pushed: $push_results
    }
}

# Show container information
def "main info" [] {
    let docker_available = check_docker_availability

    let info = {
        docker_available: $docker_available
    }

    if $docker_available {
        let docker_info = try {
            docker version --format json | from json
        } catch {
            {}
        }

        let images = try {
            docker images --filter "reference=provisioning/*" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" | lines | skip 1
        } catch {
            []
        }

        $info | insert docker_info $docker_info | insert provisioning_images $images
    } else {
        $info
    }
}

# List built containers
def "main list" [--all = false] {
    let filter = if $all { "" } else { "reference=provisioning/*" }

    let docker_available = check_docker_availability

    if not $docker_available {
        return { error: "Docker not available" }
    }

    try {
        let images = if $all {
            docker images --format json | lines | each { from json }
        } else {
            docker images --filter "reference=provisioning/*" --format json | lines | each { from json }
        }

        $images | select Repository Tag Size CreatedAt
    } catch {
        []
    }
}
