#!/usr/bin/env nu

# Docker Cross-Compilation Manager for Nushell Plugins
# Manages Docker-based cross-compilation environment and builds

# Load build targets configuration
def load_targets_config [] {
    if not ("etc/build_targets.toml" | path exists) {
        error make {msg: "Build targets configuration not found: etc/build_targets.toml"}
    }
    open etc/build_targets.toml
}

# Check if Docker is available and running
def check_docker [] {
    try {
        let version = (docker --version | complete)
        if $version.exit_code != 0 {
            return {available: false, reason: "Docker command failed"}
        }

        let info = (docker info | complete)
        if $info.exit_code != 0 {
            return {available: false, reason: "Docker daemon not running"}
        }

        {available: true, reason: null}
    } catch {|err|
        {available: false, reason: $"Docker not found: ($err.msg)"}
    }
}

# Build the Docker cross-compilation image
def build_docker_image [
    --force (-f)                # Force rebuild of image
    --no-cache                  # Don't use Docker build cache
    --verbose (-v)              # Verbose output
] {
    let config = load_targets_config
    let docker_config = $config.docker
    let dockerfile = $docker_config.dockerfile
    let image_tag = $docker_config.image_tag

    print $"🐳 Building Docker cross-compilation image..."
    print $"📄 Dockerfile: ($dockerfile)"
    print $"🏷️  Tag: ($image_tag)"

    # Check if Dockerfile exists
    if not ($dockerfile | path exists) {
        error make {msg: $"Dockerfile not found: ($dockerfile)"}
    }

    # Check if image already exists
    let existing_image = (docker images $image_tag --format "{{.Repository}}:{{.Tag}}" | complete)
    if $existing_image.exit_code == 0 and ($existing_image.stdout | str trim | str length) > 0 and not $force {
        print $"✅ Docker image already exists: ($image_tag)"
        print "💡 Use --force to rebuild or --no-cache for fresh build"
        return {success: true, image: $image_tag, rebuilt: false}
    }

    let start_time = date now

    try {
        mut build_args = ["docker", "build", "-f", $dockerfile, "-t", $image_tag]

        if $no_cache {
            $build_args = ($build_args | append "--no-cache")
        }

        # Add cache-from if specified
        if ($docker_config.cache_from? | default [] | length) > 0 {
            for cache in $docker_config.cache_from {
                $build_args = ($build_args | append ["--cache-from", $cache])
            }
        }

        # Add build args if specified
        if ($docker_config.build_args? | default [] | length) > 0 {
            for arg in $docker_config.build_args {
                $build_args = ($build_args | append ["--build-arg", $arg])
            }
        }

        $build_args = ($build_args | append ".")

        if $verbose {
            print $"🔨 Build command: ($build_args | str join ' ')"
        }

        print "🔨 Building Docker image (this may take several minutes)..."
        let result = (run-external "docker" ...($build_args | skip 1))

        let end_time = date now
        let duration = $end_time - $start_time

        print $"✅ Docker image built successfully: ($image_tag)"
        print $"⏱️  Build time: ($duration)"

        # Show image info
        let image_info = (docker images $image_tag --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}")
        print $"📊 Image info:"
        print $image_info

        {success: true, image: $image_tag, rebuilt: true, duration: $duration}
    } catch {|err|
        print $"❌ Failed to build Docker image: ($err.msg)"
        {success: false, image: null, error: $err.msg}
    }
}

# Get all plugin directories
def get_plugin_directories [] {
    glob "nu_plugin_*" | where ($it | path type) == "dir"
}

# Build a single plugin in Docker for a specific target
def build_plugin_in_docker [
    plugin_dir: string,
    target_name: string,
    image_tag: string,
    verbose: bool = false
] {
    let config = load_targets_config
    let target_config = $config.targets | get $target_name

    print $"🐳 Building ($plugin_dir) for ($target_name) in Docker..."

    let start_time = date now
    let work_dir = "/workspace"
    let source_dir = $"/workspace/($plugin_dir)"

    # Prepare Docker run command
    mut docker_args = [
        "docker", "run", "--rm",
        "-v", $"(pwd):($work_dir)",
        "-w", $source_dir,
        $image_tag
    ]

    # Set environment variables for the target
    let env_vars = $config.environment? | default {}
    let target_env = $env_vars | get $target_name? | default {}

    for var in ($target_env | transpose key value) {
        $docker_args = ($docker_args | append ["-e", $"($var.key)=($var.value)"])
    }

    # Add common environment variables
    $docker_args = ($docker_args | append [
        "-e", "RUST_BACKTRACE=1",
        "-e", "CARGO_INCREMENTAL=0"
    ])

    # Build command
    let build_cmd = ["cargo", "build", "--release", "--target", $target_config.rust_target]
    $docker_args = ($docker_args | append $build_cmd)

    try {
        if $verbose {
            print $"  🔨 Docker command: ($docker_args | str join ' ')"
        }

        let result = (run-external "docker" ...($docker_args | skip 1))

        let end_time = date now
        let duration = $end_time - $start_time

        # Check if binary was created
        let binary_name = if ($target_config | get binary_extension? | default "") != "" {
            $"($plugin_dir)($target_config.binary_extension)"
        } else {
            $plugin_dir
        }

        let binary_path = $"($plugin_dir)/target/($target_config.rust_target)/release/($binary_name)"

        if ($binary_path | path exists) {
            let size = (ls $binary_path | get 0.size)
            print $"✅ Built ($plugin_dir) for ($target_name) in ($duration) → ($size)"

            {
                plugin: $plugin_dir,
                target: $target_name,
                status: "success",
                duration: $duration,
                binary_path: $binary_path,
                size: $size,
                error: null
            }
        } else {
            error make {msg: $"Binary not found at expected path: ($binary_path)"}
        }
    } catch {|err|
        let end_time = date now
        let duration = $end_time - $start_time

        print $"❌ Error building ($plugin_dir) for ($target_name): ($err.msg)"

        {
            plugin: $plugin_dir,
            target: $target_name,
            status: "error",
            duration: $duration,
            binary_path: null,
            size: null,
            error: $err.msg
        }
    }
}

# Clean up Docker build artifacts
def cleanup_docker [
    --images (-i)              # Remove Docker images
    --containers (-c)          # Remove stopped containers
    --volumes (-v)             # Remove unused volumes
    --all (-a)                 # Clean everything
] {
    print "🧹 Cleaning up Docker artifacts..."

    if $all or $containers {
        print "🗑️  Removing stopped containers..."
        try {
            docker container prune -f
        } catch {
            print "⚠️  No containers to remove"
        }
    }

    if $all or $volumes {
        print "🗑️  Removing unused volumes..."
        try {
            docker volume prune -f
        } catch {
            print "⚠️  No volumes to remove"
        }
    }

    if $all or $images {
        print "🗑️  Removing unused images..."
        try {
            docker image prune -f
        } catch {
            print "⚠️  No images to remove"
        }
    }

    print "✅ Docker cleanup completed"
}

# Show Docker environment information
def show_docker_info [] {
    let docker_status = check_docker

    print "🐳 Docker Environment Information"
    print $"Status: (if $docker_status.available { '✅ Available' } else { '❌ Not available' })"

    if not $docker_status.available {
        print $"Reason: ($docker_status.reason)"
        return
    }

    try {
        print "\n📊 Docker Version:"
        docker --version

        print "\n💾 Docker System Info:"
        docker system df

        print "\n🖼️  Available Images:"
        docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"

        let config = load_targets_config
        let image_tag = $config.docker.image_tag

        print $"\n🎯 Cross-compilation Image Status:"
        let image_exists = (docker images $image_tag --format "{{.Repository}}:{{.Tag}}" | complete)
        if $image_exists.exit_code == 0 and ($image_exists.stdout | str trim | str length) > 0 {
            print $"✅ Image exists: ($image_tag)"
            let image_info = (docker images $image_tag --format "table {{.Size}}\t{{.CreatedAt}}")
            print $image_info
        } else {
            print $"❌ Image not found: ($image_tag)"
            print "💡 Run with --build-image to create it"
        }
    } catch {|err|
        print $"❌ Error getting Docker info: ($err.msg)"
    }
}

# Main function
def main [
    --target (-t): string = ""           # Specific target to build for
    --plugin (-p): string = ""           # Specific plugin to build
    --build-image (-b)                   # Build Docker image before building
    --force-rebuild (-f)                 # Force rebuild of Docker image
    --no-cache                           # Don't use Docker build cache
    --parallel                           # Build plugins in parallel
    --cleanup                            # Clean up Docker artifacts after build
    --info (-i)                          # Show Docker environment info
    --verbose (-v)                       # Verbose output
    --dry-run                           # Show what would be built
] {
    # Ensure we're in the repository root
    if not ("nu_plugin_clipboard" | path exists) {
        error make {msg: "Please run this script from the nushell-plugins repository root directory"}
    }

    print "🐳 Docker Cross-Compilation Manager for Nushell Plugins"

    # Check Docker availability
    let docker_status = check_docker
    if not $docker_status.available {
        print $"❌ Docker not available: ($docker_status.reason)"
        print "💡 Please install and start Docker to use cross-compilation"
        exit 1
    }

    # Info mode
    if $info {
        show_docker_info
        return
    }

    let config = load_targets_config
    let image_tag = $config.docker.image_tag

    # Build Docker image if requested or if it doesn't exist
    let image_exists = (docker images $image_tag --format "{{.Repository}}:{{.Tag}}" | complete)
    let should_build_image = $build_image or $force_rebuild or (
        $image_exists.exit_code != 0 or ($image_exists.stdout | str trim | str length) == 0
    )

    if $should_build_image {
        let build_result = build_docker_image --force=$force_rebuild --no-cache=$no_cache --verbose=$verbose
        if not $build_result.success {
            print "❌ Failed to build Docker image, cannot proceed"
            exit 1
        }
    }

    # Determine targets and plugins
    let available_targets = $config.targets | transpose name config | where ($it.config | get enabled? | default true)
    let docker_targets = $available_targets | where $it.config.docker_required

    let targets_to_build = if ($target | str length) > 0 {
        let selected = $docker_targets | where $it.name == $target
        if ($selected | length) == 0 {
            print $"❌ Target not found or doesn't require Docker: ($target)"
            print $"💡 Available Docker targets: ($docker_targets | get name | str join ', ')"
            exit 1
        }
        $selected
    } else {
        $docker_targets
    }

    let all_plugins = get_plugin_directories
    let plugins_to_build = if ($plugin | str length) > 0 {
        let selected = $all_plugins | where $it == $plugin
        if ($selected | length) == 0 {
            print $"❌ Plugin not found: ($plugin)"
            print $"💡 Available plugins: ($all_plugins | str join ', ')"
            exit 1
        }
        $selected
    } else {
        $all_plugins
    }

    if ($targets_to_build | length) == 0 {
        print "❓ No Docker targets to build"
        print $"💡 Available Docker targets: ($docker_targets | get name | str join ', ')"
        return
    }

    if ($plugins_to_build | length) == 0 {
        print "❓ No plugins to build"
        return
    }

    print $"\n📦 Plugins to build: ($plugins_to_build | length)"
    for plugin in $plugins_to_build {
        print $"  - ($plugin)"
    }

    print $"\n🎯 Docker targets: ($targets_to_build | length)"
    for target in $targets_to_build {
        print $"  - ($target.name): ($target.config.description)"
    }

    # Dry run mode
    if $dry_run {
        print "\n🔍 Dry run - showing build matrix:"
        for plugin in $plugins_to_build {
            for target in $targets_to_build {
                print $"  🐳 ($plugin) × ($target.name)"
            }
        }
        return
    }

    # Build plugins
    print $"\n🔨 Starting Docker builds..."
    let start_time = date now
    mut all_results = []

    if $parallel {
        print "⚡ Building in parallel mode..."
        let build_jobs = []
        for plugin in $plugins_to_build {
            for target in $targets_to_build {
                $build_jobs = ($build_jobs | append {
                    plugin: $plugin,
                    target: $target.name
                })
            }
        }

        $all_results = ($build_jobs | par-each {|job|
            build_plugin_in_docker $job.plugin $job.target $image_tag $verbose
        })
    } else {
        # Sequential builds
        for plugin in $plugins_to_build {
            for target in $targets_to_build {
                let result = build_plugin_in_docker $plugin $target.name $image_tag $verbose
                $all_results = ($all_results | append $result)
                print "---"
            }
        }
    }

    let end_time = date now
    let total_duration = $end_time - $start_time

    # Summary
    print "\n📊 Docker Build Summary:"
    let successful = $all_results | where status == "success"
    let failed = $all_results | where status == "error"

    print $"✅ Successful builds: ($successful | length)"
    print $"❌ Failed builds: ($failed | length)"
    print $"⏱️  Total time: ($total_duration)"

    if ($successful | length) > 0 {
        print "\n✅ Built binaries:"
        for success in $successful {
            print $"  - ($success.plugin) → ($success.target): ($success.size)"
        }
    }

    if ($failed | length) > 0 {
        print "\n❌ Failed builds:"
        for failure in $failed {
            print $"  - ($failure.plugin) → ($failure.target): ($failure.error)"
        }
    }

    # Cleanup if requested
    if $cleanup {
        print ""
        cleanup_docker --containers --volumes
    }

    # Exit with error if any builds failed
    if ($failed | length) > 0 {
        exit 1
    } else {
        print "\n🎉 All Docker builds completed successfully!"
        print "💡 Binaries are available in plugin/target/RUST_TARGET/release/ directories"
    }
}

if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") {
    main
}