#!/usr/bin/env nu

# Update All Plugins - Bulk Plugin Updater
# Updates all nu_plugin_* Cargo.toml dependencies to a new Nushell version
#
# Usage:
#   update_all_plugins.nu 0.108.0             # Update to specific version
#   update_all_plugins.nu 0.108.0 --auto-approve  # Skip confirmation
#   update_all_plugins.nu --list              # List current versions

use lib/common_lib.nu *

# Main entry point
def main [
    target_version?: string       # Target Nushell version (e.g., "0.108.0")
    --auto-approve                # Skip confirmation prompts
    --dry-run                     # Show what would be updated without changing
    --list                        # List current plugin versions
    --plugin: string              # Update only specific plugin
] {
    if $list {
        list_plugin_versions
        return
    }

    if ($target_version | is-empty) {
        log_error "Please specify a target version"
        show_usage
        exit 1
    }

    log_info $"🔄 Updating all plugins to Nushell ($target_version)"

    # Get all plugin directories
    let plugins = if ($plugin | is-not-empty) {
        [{ name: $plugin, path: $plugin }]
    } else {
        get_plugin_directories
    }

    if ($plugins | length) == 0 {
        log_error "No plugin directories found"
        exit 1
    }

    log_info $"Found ($plugins | length) plugins to update"

    # Show current versions
    log_info "\n📋 Current Versions:"
    for p in $plugins {
        let current = get_plugin_version $p.path
        print $"  ($p.name): ($current)"
    }

    # Confirm update
    if not $auto_approve and not $dry_run {
        print ""
        let response = input $"Update all plugins to ($target_version)? \(yes/no\): "
        if $response != "yes" {
            log_info "Update cancelled"
            exit 0
        }
    }

    # Update each plugin
    mut updated = 0
    mut failed = []

    print ""
    for p in $plugins {
        if $dry_run {
            log_info $"\(DRY RUN\) Would update ($p.name)"
            $updated = $updated + 1
        } else {
            let result = update_plugin $p.path $target_version
            if $result {
                log_success $"✓ Updated ($p.name)"
                $updated = $updated + 1
            } else {
                log_error $"✗ Failed to update ($p.name)"
                $failed = ($failed | append $p.name)
            }
        }
    }

    # Summary
    print ""
    if ($failed | length) == 0 {
        log_success $"✅ Successfully updated ($updated) plugins to ($target_version)"
    } else {
        log_warn $"⚠️  Updated ($updated) plugins, ($failed | length) failed:"
        for f in $failed {
            log_error $"  • ($f)"
        }
        exit 1
    }

    if not $dry_run {
        log_info "\n📝 Next steps:"
        log_info "  1. Build plugins: just build"
        log_info "  2. Test plugins: just test"
        log_info "  3. Create distribution: just pack-full"
    }
}

# Show usage
def show_usage [] {
    print "Usage:"
    print "  update_all_plugins.nu <version>           # Update to version"
    print "  update_all_plugins.nu <version> --auto-approve"
    print "  update_all_plugins.nu --list              # List current versions"
    print ""
    print "Examples:"
    print "  update_all_plugins.nu 0.108.0"
    print "  update_all_plugins.nu 0.108.0 --dry-run"
    print "  update_all_plugins.nu 0.108.0 --plugin nu_plugin_image"
}

# Get plugin directories
def get_plugin_directories []: nothing -> list<record> {
    ls nu_plugin_*
    | where type == dir
    | each {|row|
        {
            name: (get_plugin_name $row.name)
            path: $row.name
        }
    }
}

# Get plugin name from directory
def get_plugin_name [dir: string]: nothing -> string {
    $dir | path basename
}

# Get current plugin version
def get_plugin_version [plugin_dir: string]: nothing -> string {
    let cargo_toml = $"($plugin_dir)/Cargo.toml"

    if not ($cargo_toml | path exists) {
        return "N/A"
    }

    try {
        let cargo_data = open $cargo_toml
        let deps = $cargo_data | get dependencies

        # Try to get nu-plugin version
        if "nu-plugin" in ($deps | columns) {
            let nu_plugin_spec = $deps | get nu-plugin

            if ($nu_plugin_spec | describe) == "record" {
                return ($nu_plugin_spec | get version)
            } else {
                return $nu_plugin_spec
            }
        }

        return "unknown"
    } catch {
        return "error"
    }
}

# Update single plugin
def update_plugin [
    plugin_dir: string
    target_version: string
]: nothing -> bool {
    let cargo_toml = $"($plugin_dir)/Cargo.toml"

    if not ($cargo_toml | path exists) {
        log_error $"Cargo.toml not found in ($plugin_dir)"
        return false
    }

    try {
        # Read current Cargo.toml
        let content = open $cargo_toml

        # Update nu-* dependencies
        let updated_deps = update_nu_dependencies ($content | get dependencies) $target_version

        # Create updated content with dependency updates
        let updated_content = $content | update dependencies $updated_deps

        # Update package version ONLY if it's tracking Nushell versions
        # Extract major.minor from target (e.g., "0.109" from "0.109.1")
        let final_content = if ("package" in ($updated_content | columns)) {
            let current_version = $updated_content | get package.version

            # Extract major.minor from both versions
            let target_base = if ($target_version | str contains ".") {
                let parts = $target_version | split row "."
                if ($parts | length) >= 2 {
                    $"($parts.0).($parts.1)"
                } else {
                    ""
                }
            } else {
                ""
            }

            let current_base = if ($current_version | str contains ".") {
                let parts = $current_version | split row "."
                if ($parts | length) >= 2 {
                    $"($parts.0).($parts.1)"
                } else {
                    ""
                }
            } else {
                ""
            }

            # Only update if current version is tracking Nushell (major.minor matches 0.109.x)
            # but is NOT the target version yet
            if ($current_base == $target_base) and ($current_version != $target_version) {
                $updated_content | update package.version $target_version
            } else {
                # Keep independent versions or already-updated versions unchanged
                $updated_content
            }
        } else {
            $updated_content
        }

        # Only save if content actually changed (avoid unnecessary file timestamp updates)
        let original_toml = $content | to toml
        let new_toml = $final_content | to toml

        if $original_toml != $new_toml {
            # Content changed - save the file
            $final_content | to toml | save -f $cargo_toml
            return true
        } else {
            # No changes needed - don't touch the file
            return true
        }
    } catch {|err|
        log_error $"Error updating ($plugin_dir): ($err.msg)"
        return false
    }
}

# Update nu-* dependencies in dependencies table
def update_nu_dependencies [
    deps: record
    target_version: string
]: nothing -> record {
    mut updated_deps = $deps

    # List of nu-* crates to update
    let nu_crates = [
        "nu-plugin"
        "nu-protocol"
        "nu-engine"
        "nu-parser"
        "nu-cmd-base"
        "nu-color-config"
        "nu-utils"
        "nu-path"
        "nu-glob"
        "nu-json"
        "nu-pretty-hex"
        "nu-system"
        "nu-table"
        "nu-term-grid"
    ]

    for crate in $nu_crates {
        if $crate in ($deps | columns) {
            let spec = $deps | get $crate

            # Update version based on spec type
            if ($spec | describe) == "record" {
                # It's a detailed spec with version, path, features, etc.
                let updated_spec = $spec | update version $target_version
                $updated_deps = ($updated_deps | update $crate $updated_spec)
            } else {
                # It's just a version string
                $updated_deps = ($updated_deps | update $crate $target_version)
            }
        }
    }

    $updated_deps
}

# List all plugin versions
def list_plugin_versions [] {
    log_info "📋 Current Plugin Versions\n"

    let plugins = get_plugin_directories

    # Create table of versions
    let versions = $plugins | each {|p|
        {
            plugin: $p.name
            nu_plugin_version: (get_plugin_version $p.path)
        }
    }

    $versions | table

    print ""
    log_info $"Total plugins: ($plugins | length)"
}

# Check for version mismatches
def "main check" [] {
    log_info "🔍 Checking for version mismatches\n"

    let plugins = get_plugin_directories

    # Get all unique versions
    let versions = $plugins
        | each {|p| get_plugin_version $p.path}
        | uniq

    if ($versions | length) == 1 {
        log_success $"✅ All plugins use consistent version: ($versions | first)"
    } else {
        log_warn $"⚠️  Version mismatch detected! Found ($versions | length) different versions:"

        for v in $versions {
            let count = $plugins | each {|p| get_plugin_version $p.path} | where {|ver| $ver == $v} | length
            log_info $"  ($v): ($count) plugins"
        }

        # Show which plugins have which versions
        print "\nDetailed breakdown:"
        for p in $plugins {
            let version = get_plugin_version $p.path
            print $"  ($p.name): ($version)"
        }
    }
}

# Update to match nushell submodule version
def "main sync" [] {
    log_info "🔄 Syncing plugin versions with nushell submodule\n"

    # Get version from nushell submodule
    let nushell_cargo = "./nushell/Cargo.toml"

    if not ($nushell_cargo | path exists) {
        log_error "Nushell submodule not found at ./nushell/"
        log_info "Download it first with: ./scripts/download_nushell.nu"
        exit 1
    }

    let nushell_version = try {
        open $nushell_cargo | get package.version
    } catch {
        log_error "Failed to read nushell version"
        exit 1
    }

    log_info $"Nushell submodule version: ($nushell_version)"
    log_info "Updating all plugins to match...\n"

    # Run main update
    main $nushell_version --auto-approve
}
