#!/usr/bin/env nu

# Collect Full Binaries Script
# Collects nushell binary, workspace plugins, and custom plugins for full distribution

use lib/common_lib.nu [log_info, log_error, log_success, log_warn, validate_nushell_version]

def main [
    --platform (-p): string = ""     # Target platform (e.g., linux-x86_64, darwin-arm64)
    --output (-o): string = "distribution"  # Output directory
    --force (-f)                     # Force overwrite existing files
    --list (-l)                      # List available binaries
    --list-platforms                 # List available platforms
    --all-platforms                  # Collect for all available platforms
    --include-nushell (-n)           # Include nushell binary (default: true)
    --release (-r)                   # Use release builds (default: debug)
    --profile: string = ""          # Build profile to use
] {
    # Convert flags to variables for easier use
    let force_flag = ($force | default false)
    let list_flag = ($list | default false)
    let list_platforms_flag = ($list_platforms | default false)
    let all_platforms_flag = ($all_platforms | default false)
    let include_nushell_flag = true # always include nushell binary by default
    let release_flag = ($release | default false)
    # Validate nushell version consistency first
    validate_nushell_version

    if $list_platforms_flag {
        list_available_platforms
        return
    }

    if $list_flag {
        list_available_binaries $platform $release_flag $profile
        return
    }

    if $all_platforms_flag {
        collect_all_platforms $output $force_flag $include_nushell_flag $release_flag $profile
        return
    }

    # Collect binaries for specific or current platform
    collect_binaries $platform $output $force_flag $include_nushell_flag $release_flag $profile
}

# List available platforms for collection
def list_available_platforms [] {
    log_info "🌍 Available platforms for collection:"
    log_info "===================================="

    let platforms = detect_available_platforms

    if ($platforms | is-empty) {
        log_warn "⚠️  No built binaries found. Build plugins first with 'just build' or 'just build-cross-all'"
        return
    }

    for platform in $platforms {
        log_info $"  ✅ ($platform.name)"
        log_info $"     Target: ($platform.target)"
        log_info $"     Nushell: ($platform.has_nushell)"
        log_info $"     Plugins: ($platform.plugin_count)"
        log_info ""
    }
}

# List available binaries for a platform
def list_available_binaries [
    platform: string
    use_release: bool
    profile: string
] {
    log_info "📋 Available binaries:"
    log_info "====================="

    let current_platform = if ($platform | is-empty) {
        detect_current_platform
    } else {
        $platform
    }

    log_info $"Platform: ($current_platform)"
    log_info ""

    # Check nushell binary
    let nushell_info = get_nushell_binary_info $current_platform $use_release $profile
    if ($nushell_info | is-not-empty) {
        log_info "🚀 Nushell Binary:"
        log_info $"  Path: ($nushell_info.path)"
        log_info $"  Size: ($nushell_info.size)"
        log_info $"  Modified: ($nushell_info.modified)"
        log_info ""
    } else {
        log_warn "⚠️  Nushell binary not found for this platform"
        log_info ""
    }

    # Check workspace plugins
    let workspace_plugins = get_workspace_plugins_info $current_platform $use_release $profile
    if not ($workspace_plugins | is-empty) {
        log_info "🧩 Workspace Plugins:"
        for plugin in $workspace_plugins {
            log_info $"  ✅ ($plugin.name) - ($plugin.size)"
        }
        log_info ""
    }

    # Check custom plugins
    let custom_plugins = get_custom_plugins_info $current_platform $use_release $profile
    if not ($custom_plugins | is-empty) {
        log_info "🔧 Custom Plugins:"
        for plugin in $custom_plugins {
            log_info $"  ✅ ($plugin.name) - ($plugin.size)"
        }
        log_info ""
    }

    let total_count = (
        ($workspace_plugins | length) +
        ($custom_plugins | length) +
        (if ($nushell_info | is-not-empty) { 1 } else { 0 })
    )

    log_info $"📊 Total binaries: ($total_count)"
}

# Collect binaries for all platforms
def collect_all_platforms [
    output: string
    force: bool
    include_nushell: bool
    use_release: bool
    profile: string
] {
    log_info "🌍 Collecting binaries for all platforms..."

    let platforms = detect_available_platforms

    if ($platforms | is-empty) {
        log_error "❌ No built binaries found. Build first with 'just build-cross-all'"
        exit 1
    }

    for platform in $platforms {
        log_info $"📦 Collecting for ($platform.name)..."
        try {
            collect_binaries $platform.target $output $force $include_nushell $use_release $profile
        } catch { |err|
            log_error $"❌ Failed to collect ($platform.name): (($err | get msg | default 'Unknown error'))"
        }
    }

    log_success "✅ Collection complete for all platforms"
}

# Main collection function
def collect_binaries [
    platform: string
    output: string
    force: bool
    include_nushell: bool
    use_release: bool
    profile: string
] {
    let target_platform = if ($platform | is-empty) {
        detect_current_platform
    } else {
        $platform
    }

    log_info $"📦 Collecting binaries for platform: ($target_platform)"

    # Create output structure
    let platform_dir = $"($output)/($target_platform)"
    create_target_structure $platform_dir $force

    mut collected_files = []

    # Collect nushell binary if requested
    if $include_nushell {
        let nushell_info = get_nushell_binary_info $target_platform $use_release $profile
        if ($nushell_info | is-not-empty) {
            let dest_path = $"($platform_dir)/nu"
            let dest_path = if $nu.os-info.name == "windows" { $"($dest_path).exe" } else { $dest_path }

            copy_binary $nushell_info.path $dest_path $force
            $collected_files = ($collected_files | append {
                name: "nu"
                type: "nushell"
                path: $dest_path
                size: (get_file_size $dest_path)
            })
            log_success $"✅ Collected nushell binary"
        } else {
            log_warn "⚠️  Nushell binary not found for platform ($target_platform)"
        }
    }

    # Collect workspace plugins
    let workspace_plugins = get_workspace_plugins_info $target_platform $use_release $profile
    for plugin in $workspace_plugins {
        let dest_path = $"($platform_dir)/($plugin.name)"
        copy_binary $plugin.path $dest_path $force
        $collected_files = ($collected_files | append {
            name: $plugin.name
            type: "workspace_plugin"
            path: $dest_path
            size: (get_file_size $dest_path)
        })
    }

    # Collect custom plugins
    let custom_plugins = get_custom_plugins_info $target_platform $use_release $profile
    for plugin in $custom_plugins {
        let dest_path = $"($platform_dir)/($plugin.name)"
        copy_binary $plugin.path $dest_path $force
        $collected_files = ($collected_files | append {
            name: $plugin.name
            type: "custom_plugin"
            path: $dest_path
            size: (get_file_size $dest_path)
        })
    }

    # Copy additional files
    copy_additional_files $platform_dir $force

    # Create manifest
    create_manifest $platform_dir $target_platform $collected_files $include_nushell

    # Create installation script
    create_installation_scripts $platform_dir

    # Summary
    let total_size = ($collected_files | get size | math sum)
    log_success $"✅ Collection complete for ($target_platform)"
    log_info $"📊 Collected ($collected_files | length) binaries"
    log_info $"📁 Output directory: ($platform_dir)"
    log_info $"💾 Total size: ($total_size)"

    return {
        platform: $target_platform
        output_dir: $platform_dir
        files: $collected_files
        total_size: $total_size
    }
}

# Get nushell binary information
def get_nushell_binary_info [
    platform: string
    use_release: bool
    profile: string
]: nothing -> record {
    let nushell_dir = $"($env.PWD)/nushell"
    mut target_dir = $"($nushell_dir)/target"

    # Handle cross-compilation targets
    let target_triple = convert_platform_to_target $platform
    if ($target_triple | is-not-empty) and $target_triple != (detect_current_target) {
        $target_dir = $"($target_dir)/($target_triple)"
    }

    # Determine profile directory
    mut profile_dir = if ($profile | is-not-empty) {
        $profile
    } else if $use_release {
        "release"
    } else {
        "debug"
    }

    let binary_name = if ($platform | str contains "windows") or $nu.os-info.name == "windows" {
        "nu.exe"
    } else {
        "nu"
    }

    let binary_path = $"($target_dir)/($profile_dir)/($binary_name)"

    if ($binary_path | path exists) {
        let file_info = ls $binary_path | get 0
        return {
            name: "nu"
            path: $binary_path
            size: $file_info.size
            modified: $file_info.modified
        }
    }

    return {}
}

# Get workspace plugins information
def get_workspace_plugins_info [
    platform: string
    use_release: bool
    profile: string
]: nothing -> list<record> {
    let nushell_dir = $"($env.PWD)/nushell"
    mut target_dir = $"($nushell_dir)/target"

    # Handle cross-compilation targets
    let target_triple = convert_platform_to_target $platform
    if ($target_triple | is-not-empty) and $target_triple != (detect_current_target) {
        $target_dir = $"($target_dir)/($target_triple)"
    }

    # Determine profile directory
    mut profile_dir = if ($profile | is-not-empty) {
        $profile
    } else if $use_release {
        "release"
    } else {
        "debug"
    }

    let workspace_plugins = [
        "nu_plugin_custom_values"
        "nu_plugin_example"
        "nu_plugin_formats"
        "nu_plugin_gstat"
        "nu_plugin_inc"
        "nu_plugin_polars"
        "nu_plugin_query"
        "nu_plugin_stress_internals"
    ]

    mut found_plugins = []

    for plugin in $workspace_plugins {
        let binary_name = if ($platform | str contains "windows") or $nu.os-info.name == "windows" {
            $"($plugin).exe"
        } else {
            $plugin
        }

        let binary_path = $"($target_dir)/($profile_dir)/($binary_name)"

        if ($binary_path | path exists) {
            let file_info = ls $binary_path | get 0
            $found_plugins = ($found_plugins | append {
                name: $plugin
                path: $binary_path
                size: $file_info.size
                modified: $file_info.modified
            })
        }
    }

    return $found_plugins
}

# Get custom plugins information
def get_custom_plugins_info [
    platform: string
    use_release: bool
    profile: string
]: nothing -> list<record> {
    # Get list of plugin directories (nu_plugin_*)
    let plugin_dirs = ls nu_plugin_*
        | where type == "dir"
        | where name != "nushell"  # Exclude nushell submodule
        | get name

    mut found_plugins = []
    let target_triple = convert_platform_to_target $platform

    for plugin_dir in $plugin_dirs {
        mut target_dir = $"($plugin_dir)/target"

        # Handle cross-compilation targets
        if ($target_triple | is-not-empty) and $target_triple != (detect_current_target) {
            $target_dir = $"($target_dir)/($target_triple)"
        }

        # Determine profile directory
        mut profile_dir = if ($profile | is-not-empty) {
            $profile
        } else if $use_release {
            "release"
        } else {
            "debug"
        }

        let binary_name = if ($platform | str contains "windows") or $nu.os-info.name == "windows" {
            $"($plugin_dir).exe"
        } else {
            $plugin_dir
        }

        let binary_path = $"($target_dir)/($profile_dir)/($binary_name)"

        if ($binary_path | path exists) {
            let file_info = ls $binary_path | get 0
            $found_plugins = ($found_plugins | append {
                name: $plugin_dir
                path: $binary_path
                size: $file_info.size
                modified: $file_info.modified
            })
        }
    }

    return $found_plugins
}

# Utility functions
def detect_current_platform []: nothing -> string {
    let os = $nu.os-info.name
    let arch = $nu.os-info.arch

    match [$os, $arch] {
        ["linux", "x86_64"] => "linux-x86_64"
        ["linux", "aarch64"] => "linux-aarch64"
        ["macos", "x86_64"] => "darwin-x86_64"
        ["macos", "aarch64"] => "darwin-arm64"
        ["windows", "x86_64"] => "windows-x86_64"
        ["windows", "aarch64"] => "windows-aarch64"
        _ => $"($os)-($arch)"
    }
}

def detect_current_target []: nothing -> string {
    let os = $nu.os-info.name
    let arch = $nu.os-info.arch

    match [$os, $arch] {
        ["linux", "x86_64"] => "x86_64-unknown-linux-gnu"
        ["linux", "aarch64"] => "aarch64-unknown-linux-gnu"
        ["macos", "x86_64"] => "x86_64-apple-darwin"
        ["macos", "aarch64"] => "aarch64-apple-darwin"
        ["windows", "x86_64"] => "x86_64-pc-windows-msvc"
        ["windows", "aarch64"] => "aarch64-pc-windows-msvc"
        _ => $"($arch)-unknown-($os)"
    }
}

def convert_platform_to_target [platform: string]: nothing -> string {
    match $platform {
        "linux-x86_64" => "x86_64-unknown-linux-gnu"
        "linux-aarch64" => "aarch64-unknown-linux-gnu"
        "darwin-x86_64" => "x86_64-apple-darwin"
        "darwin-arm64" => "aarch64-apple-darwin"
        "windows-x86_64" => "x86_64-pc-windows-msvc"
        "windows-aarch64" => "aarch64-pc-windows-msvc"
        _ => $platform
    }
}

def detect_available_platforms []: nothing -> list<record> {
    mut platforms = []

    # Check nushell target directory for built platforms
    let nushell_target = $"($env.PWD)/nushell/target"
    if ($nushell_target | path exists) {
        let target_dirs = try { ls $nushell_target | where type == "dir" | get name } catch { [] }

        for target_dir in $target_dirs {
            if ($target_dir | str ends-with "target") { continue }  # Skip base target dir

            let target_name = ($target_dir | path basename)
            let platform_name = convert_target_to_platform $target_name

            # Check if this target has binaries
            let release_dir = $"($target_dir)/release"
            let debug_dir = $"($target_dir)/debug"

            let has_release = ($release_dir | path exists) and (try { ls $release_dir | where name =~ "nu" | length } catch { 0 }) > 0
            let has_debug = ($debug_dir | path exists) and (try { ls $debug_dir | where name =~ "nu" | length } catch { 0 }) > 0

            if $has_release or $has_debug {
                $platforms = ($platforms | append {
                    name: $platform_name
                    target: $target_name
                    has_nushell: true
                    plugin_count: 0  # Will be calculated
                })
            }
        }
    }

    # If no cross-compiled targets found, add current platform
    if ($platforms | is-empty) {
        let current_platform = detect_current_platform
        let current_target = detect_current_target

        $platforms = ($platforms | append {
            name: $current_platform
            target: $current_target
            has_nushell: true
            plugin_count: 0
        })
    }

    return $platforms
}

def convert_target_to_platform [target: string]: nothing -> string {
    match $target {
        "x86_64-unknown-linux-gnu" => "linux-x86_64"
        "aarch64-unknown-linux-gnu" => "linux-aarch64"
        "x86_64-apple-darwin" => "darwin-x86_64"
        "aarch64-apple-darwin" => "darwin-arm64"
        "x86_64-pc-windows-msvc" => "windows-x86_64"
        "aarch64-pc-windows-msvc" => "windows-aarch64"
        _ => $target
    }
}

def create_target_structure [target_path: string, force: bool] {
    if ($target_path | path exists) and not $force {
        log_error $"Target directory exists: ($target_path). Use --force to overwrite."
        exit 1
    }

    if ($target_path | path exists) and $force {
        rm -rf $target_path
        log_warn $"🗑️  Removed existing directory: ($target_path)"
    }

    mkdir $target_path
    log_info $"📁 Created target directory: ($target_path)"

    # Create subdirectories
    let subdirs = ["scripts", "config", "docs"]
    for subdir in $subdirs {
        mkdir $"($target_path)/($subdir)"
    }
}

def copy_binary [src: string, dest: string, force: bool] {
    if ($dest | path exists) and not $force {
        log_warn $"⚠️  File exists, skipping: ($dest)"
        return
    }

    cp $src $dest
    chmod +x $dest
}

def copy_additional_files [target_path: string, force: bool] {
    let files_to_copy = [
        {src: "LICENSE", dest: "LICENSE", required: false}
        {src: "README.md", dest: "README.md", required: false}
        {src: "CHANGELOG.md", dest: "CHANGELOG.md", required: false}
    ]

    for file in $files_to_copy {
        if ($file.src | path exists) {
            let dest_path = $"($target_path)/($file.dest)"
            if not ($dest_path | path exists) or $force {
                cp $file.src $dest_path
                log_info $"  📄 Copied ($file.src) → ($file.dest)"
            }
        }
    }
}

def create_manifest [
    target_path: string
    platform: string
    files: list<record>
    includes_nushell: bool
] {
    let manifest = {
        platform: $platform
        created: (date now | format date %Y-%m-%d_%H-%M-%S)
        includes_nushell: $includes_nushell
        total_files: ($files | length)
        files: $files
        nushell_version: (try {
            if $includes_nushell {
                let nu_binary = ($files | where type == "nushell" | get 0)
                if ($nu_binary | is-not-empty) {
                    (run-external $nu_binary.path "--version" | complete | get stdout | str trim)
                } else {
                    "unknown"
                }
            } else {
                "not_included"
            }
        } catch { "unknown" })
    }

    $manifest | to json | save --force $"($target_path)/manifest.json"
    log_info $"📋 Created manifest: ($target_path)/manifest.json"
}

def create_installation_scripts [target_path: string] {
    # Create a basic installation script that registers plugins
    let install_script = '#!/usr/bin/env nu

# Installation script for nushell and plugins
# This script registers all plugins with the nushell binary

def main [
    --verify  # Verify installation after completion
] {
    print "🚀 Installing Nushell and plugins..."

    # Get current directory (should be the distribution directory)
    let dist_dir = $env.PWD

    # Find nushell binary
    let nu_binary = if ("nu" | path exists) {
        "./nu"
    } else if ("nu.exe" | path exists) {
        "./nu.exe"
    } else {
        print "❌ Nushell binary not found in distribution"
        exit 1
    }

    # Find plugin binaries
    let plugins = ls . | where type == "file" | where name =~ "nu_plugin_" | get name

    print $"📦 Found ($plugins | length) plugins to register"

    # Register each plugin
    for plugin in $plugins {
        print $"  Registering ($plugin)..."
        try {
            run-external $nu_binary "plugin" "add" $"./($plugin)"
        } catch { |err|
            print $"  ⚠️  Failed to register ($plugin): ($err.msg)"
        }
    }

    if $verify {
        print "🔍 Verifying installation..."
        try {
            let plugin_list = run-external $nu_binary "-c" "plugin list" | complete
            if $plugin_list.exit_code == 0 {
                print "✅ Plugin verification successful"
                print $plugin_list.stdout
            } else {
                print "❌ Plugin verification failed"
                print $plugin_list.stderr
            }
        } catch { |err|
            print $"❌ Verification failed: ($err.msg)"
        }
    }

    print "✅ Installation complete!"
}
'

    $install_script | save --force $"($target_path)/install.nu"
    chmod +x $"($target_path)/install.nu"
    log_info $"📜 Created installation script: ($target_path)/install.nu"
}

def get_file_size [path: string]: nothing -> int {
    try {
        let file_info = ls $path | get 0
        $file_info.size
    } catch {
        0
    }
}