#!/usr/bin/env nu

# Cross-Platform Build Script for Nushell Plugins
# Orchestrates cross-compilation for multiple platforms using Rust targets and Docker

# Version check - mandatory for all plugin operations
def version_check [] {
    try {
        nu scripts/check_version.nu --quiet | ignore
    } catch {
        print "❌ Nushell version mismatch detected!"
        print "🔧 Run: nu scripts/check_version.nu --fix"
        exit 1
    }
}

# 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
}

# Get host platform information
def get_host_info [] {
    let arch = match (uname -m) {
        "x86_64" => "amd64",
        "aarch64" => "arm64",
        "arm64" => "arm64",
        $arch if ($arch | str starts-with "arm") => "arm64",
        $other => $other
    }

    let platform = match (uname -s | str downcase) {
        $os if ($os | str contains "linux") => "linux",
        $os if ($os | str contains "darwin") => "darwin",
        $os if ($os | str contains "windows") => "windows",
        $other => $other
    }

    {platform: $platform, arch: $arch, full: $"($platform)-($arch)"}
}

# Validate if target can be built on current host
def validate_target [target: record, host_info: record] {
    # Check if target requires specific host
    if ($target | get host_required? | default null) != null {
        if $target.host_required != $host_info.platform {
            return {
                valid: false,
                reason: $"Target requires ($target.host_required) host, but running on ($host_info.platform)"
                suggestion: "Use Docker build or switch to appropriate host"
            }
        }
    }

    # Check if native build is possible
    if $target.native_build and not $target.docker_required {
        # Check if rust target is installed
        let installed_targets = (rustup target list --installed | lines)
        if not ($target.rust_target in $installed_targets) {
            return {
                valid: false,
                reason: $"Rust target ($target.rust_target) not installed"
                suggestion: $"Run: rustup target add ($target.rust_target)"
            }
        }
    }

    {valid: true, reason: null, suggestion: null}
}

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

# Build a single plugin for a specific target
def build_plugin_for_target [
    plugin_dir: string,
    target: record,
    use_docker: bool = false,
    verbose: bool = false
] {
    print $"🔨 Building ($plugin_dir) for ($target.platform_name)..."

    let start_time = date now
    let original_dir = pwd

    try {
        cd $plugin_dir

        mut build_cmd = ["cargo", "build", "--release"]

        if not $use_docker {
            $build_cmd = ($build_cmd | append ["--target", $target.rust_target])
        }

        # Set environment variables if specified
        let config = load_targets_config
        let env_vars = $config.environment? | default {}
        let target_env = $env_vars | get $target.platform_name? | default {}

        mut env_settings = []
        for var in ($target_env | transpose key value) {
            $env_settings = ($env_settings | append $"($var.key)=($var.value)")
        }

        if $verbose {
            print $"  Command: ($build_cmd | str join ' ')"
            if ($env_settings | length) > 0 {
                print $"  Environment: ($env_settings | str join ', ')"
            }
        }

        # Execute build command
        if $use_docker {
            # Docker build will be handled by build_docker_cross.nu
            let docker_result = nu ../scripts/build_docker_cross.nu --target $target.platform_name --plugin $plugin_dir
            if $docker_result.exit_code != 0 {
                error make {msg: $"Docker build failed for ($plugin_dir)"}
            }
        } else {
            # Native cross-compilation
            with-env ($target_env) {
                run-external "cargo" ...$build_cmd
            }
        }

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

        print $"✅ Built ($plugin_dir) for ($target.platform_name) in ($duration)"

        {
            plugin: $plugin_dir,
            target: $target.platform_name,
            status: "success",
            duration: $duration,
            error: null
        }
    } catch {|err|
        cd $original_dir
        print $"❌ Error building ($plugin_dir) for ($target.platform_name): ($err.msg)"

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

# Check if Docker is available and running
def check_docker [] {
    try {
        docker --version | ignore
        docker info | ignore
        true
    } catch {
        false
    }
}

# Install missing Rust targets
def install_rust_targets [targets: list<string>] {
    print "🦀 Installing missing Rust targets..."

    let installed = (rustup target list --installed | lines)
    let missing = ($targets | where $it not-in $installed)

    if ($missing | length) > 0 {
        print $"📥 Installing targets: ($missing | str join ', ')"
        for target in $missing {
            print $"  Installing ($target)..."
            rustup target add $target
        }
        print "✅ All targets installed"
    } else {
        print "✅ All required targets already installed"
    }
}

# Get binary path for a plugin and target
def get_binary_path [plugin_dir: string, target: record] {
    let binary_name = if ($target | get binary_extension? | default "") != "" {
        $"($plugin_dir)($target.binary_extension)"
    } else {
        $plugin_dir
    }

    $"($plugin_dir)/target/($target.rust_target)/release/($binary_name)"
}

# Verify built binaries exist
def verify_built_binaries [results: list<record>] {
    print "🔍 Verifying built binaries..."

    let config = load_targets_config
    mut verification_results = []

    for result in $results {
        if $result.status == "success" {
            let target_config = $config.targets | get $result.target
            let binary_path = get_binary_path $result.plugin $target_config

            if ($binary_path | path exists) {
                let size = (ls $binary_path | get 0.size)
                print $"  ✅ ($result.plugin) → ($result.target): ($size)"
                $verification_results = ($verification_results | append {
                    plugin: $result.plugin,
                    target: $result.target,
                    binary_path: $binary_path,
                    size: $size,
                    verified: true
                })
            } else {
                print $"  ❌ ($result.plugin) → ($result.target): Binary not found at ($binary_path)"
                $verification_results = ($verification_results | append {
                    plugin: $result.plugin,
                    target: $result.target,
                    binary_path: $binary_path,
                    size: null,
                    verified: false
                })
            }
        }
    }

    $verification_results
}

# Main function
def main [
    --targets (-t): list<string> = []    # Specific targets to build (e.g., linux-amd64, darwin-arm64)
    --all-targets (-a)                   # Build for all enabled targets
    --plugins (-p): list<string> = []    # Specific plugins to build
    --docker (-d)                        # Force use of Docker for all builds
    --native (-n)                        # Force native compilation (fail if not possible)
    --parallel                           # Build plugins in parallel (experimental)
    --install-targets                    # Install missing Rust targets automatically
    --verbose (-v)                       # Verbose output
    --dry-run                           # Show what would be built without building
    --list-targets (-l)                  # List available targets and exit
] {
    # Mandatory version check
    version_check

    # 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 "🚀 Nushell Plugins Cross-Platform Build System"

    # Load configuration
    let config = load_targets_config
    let host_info = get_host_info

    print $"🖥️  Host: ($host_info.full)"

    # List targets mode
    if $list_targets {
        print "\n📋 Available build targets:"
        for target_name in ($config.targets | transpose name config | get name) {
            let target_config = $config.targets | get $target_name
            let enabled = $target_config | get enabled? | default true
            let status = if $enabled { "✅" } else { "❌" }
            print $"  ($status) ($target_name): ($target_config.description)"
            print $"      Rust target: ($target_config.rust_target)"
            print $"      Docker required: ($target_config.docker_required)"
            print $"      Native build: ($target_config.native_build)"
        }
        return
    }

    # Determine targets to build
    let available_targets = $config.targets | transpose name config
    let enabled_targets = $available_targets | where ($it.config | get enabled? | default true)

    mut targets_to_build = if $all_targets {
        $enabled_targets
    } else if ($targets | length) > 0 {
        $enabled_targets | where $it.name in $targets
    } else {
        # Default to host platform if available
        let host_target = $enabled_targets | where $it.name == $host_info.full
        if ($host_target | length) > 0 {
            $host_target
        } else {
            # Fallback to first enabled target
            $enabled_targets | first 1
        }
    }

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

    # Determine plugins to build
    let all_plugins = get_plugin_directories
    let plugins_to_build = if ($plugins | length) > 0 {
        $all_plugins | where $it in $plugins
    } else {
        $all_plugins
    }

    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🎯 Targets to build: ($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 {
                let method = if $docker or $target.config.docker_required { "Docker" } else { "Native" }
                print $"  ($plugin) × ($target.name) → ($method)"
            }
        }
        return
    }

    # Check prerequisites
    let docker_available = check_docker
    print $"\n🔧 Prerequisites:"
    print $"  Docker: (if $docker_available { '✅' } else { '❌' })"
    print $"  Rust: ✅"

    # Validate targets and collect required Rust targets
    mut rust_targets_needed = []
    mut valid_builds = []

    for target in $targets_to_build {
        let validation = validate_target $target.config $host_info
        let use_docker = $docker or $target.config.docker_required or (not $validation.valid)

        if $native and $use_docker {
            print $"❌ Cannot build ($target.name) natively: ($validation.reason)"
            continue
        }

        if $use_docker and not $docker_available {
            print $"❌ Docker required for ($target.name) but Docker not available"
            print $"💡 ($validation.suggestion)"
            continue
        }

        if not $use_docker and $validation.valid {
            $rust_targets_needed = ($rust_targets_needed | append $target.config.rust_target)
        }

        $valid_builds = ($valid_builds | append {
            target: $target,
            use_docker: $use_docker,
            validation: $validation
        })
    }

    # Install missing Rust targets
    if ($rust_targets_needed | length) > 0 and $install_targets {
        install_rust_targets $rust_targets_needed
    }

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

    if $parallel {
        print "⚡ Building in parallel mode..."
        # Build all plugin-target combinations in parallel
        let build_jobs = []
        for plugin in $plugins_to_build {
            for build in $valid_builds {
                $build_jobs = ($build_jobs | append {
                    plugin: $plugin,
                    target: $build.target.config,
                    use_docker: $build.use_docker
                })
            }
        }

        $all_results = ($build_jobs | par-each {|job|
            build_plugin_for_target $job.plugin $job.target $job.use_docker $verbose
        })
    } else {
        # Sequential builds
        for plugin in $plugins_to_build {
            for build in $valid_builds {
                let result = build_plugin_for_target $plugin $build.target.config $build.use_docker $verbose
                $all_results = ($all_results | append $result)
                print "---"
            }
        }
    }

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

    # Verify binaries
    print "\n🔍 Verification:"
    let verification = verify_built_binaries $all_results

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

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

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

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

    # Exit with error if any builds failed
    if ($failed | length) > 0 {
        exit 1
    } else {
        print "\n🎉 All builds completed successfully!"
        print "💡 Next steps:"
        print "  - Collect binaries: just collect"
        print "  - Create packages: just pack"
        print "  - Test binaries: just test"
    }
}

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