#!/usr/bin/env nu

# Update Installed Plugins - Remove Old & Install New Versions
#
# This script updates plugins that are already installed in ~/.local/bin:
#   1. Detects installed plugins in ~/.local/bin
#   2. Removes old plugin binaries
#   3. Rebuilds them from source
#   4. Installs new versions to ~/.local/bin
#   5. Registers with nushell
#
# Usage:
#   update_installed_plugins.nu                    # Update all installed plugins
#   update_installed_plugins.nu --check            # Check only, don't update
#   update_installed_plugins.nu --plugin NAME      # Update specific plugin
#   update_installed_plugins.nu --verify           # Verify registration after update

use lib/common_lib.nu *

# Configuration
def get-install-dir []: nothing -> string {
    $"($env.HOME)/.local/bin"
}

def get-plugin-dirs []: nothing -> list<record> {
    ls nu_plugin_*
    | where type == "dir"
    | each {|row|
        {
            name: $row.name
            path: $row.name
            cargo_toml: $"($row.name)/Cargo.toml"
        }
    }
}

# Main entry point
def main [
    --check (-c)                    # Check only, don't modify
    --plugin: string = ""           # Update specific plugin (optional)
    --verify (-v)                   # Verify registration after update
    --no-register                   # Skip registration step
    --force (-f)                    # Force rebuild even if no changes
] {
    log_info "🔄 Plugin Update Manager - Remove Old & Install New"
    log_info "=================================================================="

    let install_dir = (get-install-dir)

    # Check install directory exists
    if not ($install_dir | path exists) {
        log_error $"Install directory not found: ($install_dir)"
        log_info "Creating directory..."
        mkdir $install_dir
    }

    log_info $"📍 Install directory: ($install_dir)"

    # Step 1: Find installed plugins
    log_info "\n📋 Step 1: Detecting installed plugins..."
    let installed = detect_installed_plugins $install_dir

    if ($installed | length) == 0 {
        log_warn $"No installed plugins found in ($install_dir)"
        return
    }

    log_success $"Found ($installed | length) installed plugin\(s\):"
    for plugin in $installed {
        log_info $"  • ($plugin.name) \(installed: ($plugin.install_path)\)"
    }

    # Step 2: Find source plugins
    log_info "\n🔍 Step 2: Finding plugin sources..."
    let sources = (get-plugin-dirs)

    if ($sources | length) == 0 {
        log_error "No plugin sources found in current directory"
        exit 1
    }

    log_success $"Found ($sources | length) plugin source\(s\)"

    # Step 3: Match installed with sources
    log_info "\n🔗 Step 3: Matching installed plugins with sources..."
    let to_update = match_plugins_with_sources $installed $sources $plugin

    if ($to_update | length) == 0 {
        if ($plugin | is-not-empty) {
            log_error $"Plugin not found or not installed: ($plugin)"
        } else {
            log_warn "No plugins to update"
        }
        return
    }

    log_success $"Ready to update ($to_update | length) plugin\(s\):"
    for item in $to_update {
        log_info $"  • ($item.name)"
        log_info $"    Source: ($item.source.path)"
        log_info $"    Target: ($item.install_path)"
    }

    # Confirmation
    if not $check {
        print ""
        log_info "🤔 Proceed with update? (yes/no): "
        let response = try {
            input ""
        } catch {
            "yes"
        }
        if $response != "yes" {
            log_info "Update cancelled"
            return
        }
    }

    # Step 4: Remove old binaries
    if not $check {
        log_info "\n🗑️  Step 4: Removing old plugin binaries..."
        remove_old_plugins $to_update $install_dir
    } else {
        log_info "\n🗑️  Step 4 \(DRY RUN\): Would remove old plugin binaries..."
        for item in $to_update {
            log_info $"  Would remove: ($item.install_path)"
        }
    }

    # Step 5: Rebuild plugins
    if not $check {
        log_info "\n🔨 Step 5: Building updated plugins..."
        build_plugins $to_update $force
    } else {
        log_info "\n🔨 Step 5 \(DRY RUN\): Would build plugins..."
        for item in $to_update {
            log_info $"  Would build: ($item.name) in ($item.source.path)"
        }
    }

    # Step 6: Install new binaries
    if not $check {
        log_info "\n📦 Step 6: Installing new plugin binaries..."
        install_new_plugins $to_update $install_dir
    } else {
        log_info "\n📦 Step 6 \(DRY RUN\): Would install new binaries..."
        for item in $to_update {
            let binary_name = if ($item.source.name | str starts-with "nu_plugin_") {
                $item.source.name
            } else {
                $"nu_plugin_($item.source.name)"
            }
            log_info $"  Would install: ($item.source.path)/target/release/($binary_name)"
        }
    }

    # Step 7: Register plugins
    if not $check and not $no_register {
        log_info "\n🔌 Step 7: Registering plugins with nushell..."
        register_updated_plugins $to_update $install_dir $verify
    } else if not $check {
        log_info "\n🔌 Step 7: Skipped (--no-register)"
    } else {
        log_info "\n🔌 Step 7 \(DRY RUN\): Would register plugins..."
    }

    # Final summary
    log_info "\n=================================================================="
    if $check {
        log_info "✅ DRY RUN COMPLETE - No changes made"
    } else {
        log_success "✅ Plugin update complete!"
    }
    log_info "Next steps:"
    log_info "  1. Restart nushell: exit && nu"
    log_info "  2. Verify plugins: nu -c 'plugin list'"
}

# Detect installed plugins in ~/.local/bin
def detect_installed_plugins [
    install_dir: string
]: nothing -> list<record> {
    try {
        ls $install_dir
        | where type == "file"
        | where {|row|
            let basename = $row.name | path basename
            $basename =~ "^nu_plugin_"
        }
        | each {|row|
            let basename = $row.name | path basename
            {
                name: $basename
                install_path: $row.name
            }
        }
    } catch {
        []
    }
}

# Match installed plugins with source directories
def match_plugins_with_sources [
    installed: list<record>
    sources: list<record>
    filter_plugin: string
]: nothing -> list<record> {
    let filtered_sources = if ($filter_plugin | is-not-empty) {
        $sources | where {|s|
            if $s.name == $filter_plugin {
                true
            } else {
                $s.name | str ends-with $filter_plugin
            }
        }
    } else {
        $sources
    }

    $installed
    | where {|inst|
        let match = $filtered_sources | where {|src|
            if $inst.name == $src.name {
                true
            } else if $inst.name == ($src.name | str replace "^nu_plugin_" "") {
                true
            } else {
                false
            }
        } | first

        $match != null
    }
    | each {|inst|
        let source = $filtered_sources | where {|src|
            if $inst.name == $src.name {
                true
            } else if $inst.name == ($src.name | str replace "^nu_plugin_" "") {
                true
            } else {
                false
            }
        } | first

        {
            name: $inst.name
            install_path: $inst.install_path
            source: $source
        }
    }
}

# Remove old plugin binaries
def remove_old_plugins [
    to_update: list<record>
    install_dir: string
] {
    for item in $to_update {
        if ($item.install_path | path exists) {
            log_info $"  Removing: ($item.name)"
            try {
                rm $item.install_path
                log_success $"    ✓ Deleted"
            } catch {|err|
                log_error $"    ✗ Failed: ($err.msg)"
            }
        }
    }
}

# Build plugins from source
def build_plugins [
    to_update: list<record>
    force: bool
] {
    for item in $to_update {
        let plugin_name = $item.source.name
        let plugin_path = $item.source.path

        log_info $"  Building: ($plugin_name)"

        try {
            cd $plugin_path

            # Clean if force rebuild
            if $force {
                log_info $"    Cleaning build artifacts..."
                cargo clean
            }

            # Build release version
            log_info $"    Compiling..."
            try {
                cargo build --release out+err>| grep -E "(Compiling|Finished)"
            } catch {
                log_info $"    Build output not parseable, continuing..."
            }

            cd - | ignore
            log_success $"    ✓ Built successfully"
        } catch {|err|
            log_error $"    ✗ Build failed: ($err.msg)"
        }
    }
}

# Install new plugin binaries
def install_new_plugins [
    to_update: list<record>
    install_dir: string
] {
    for item in $to_update {
        let plugin_name = $item.source.name
        let plugin_path = $item.source.path
        let plugin_binary = $"($plugin_path)/target/release/($plugin_name)"
        let target_path = $item.install_path

        if not ($plugin_binary | path exists) {
            log_error $"  Build artifact not found: ($plugin_binary)"
            continue
        }

        try {
            log_info $"  Installing: ($plugin_name)"
            cp $plugin_binary $target_path
            chmod +x $target_path
            log_success $"    ✓ Installed to ($target_path)"
        } catch {|err|
            log_error $"    ✗ Installation failed: ($err.msg)"
        }
    }
}

# Register updated plugins with nushell
def register_updated_plugins [
    to_update: list<record>
    install_dir: string
    verify: bool
] {
    let results = $to_update | each {|item|
        let plugin_path = $item.install_path
        let plugin_name = ($item.name | str replace "^nu_plugin_" "")

        log_info $"  Registering: ($item.name)"

        let reg_result = try {
            # Remove old registration if exists
            try {
                nu -c $"plugin rm ($plugin_name)" out+err>| null
            } catch {
                # Ignore if plugin doesn't exist
            }

            # Add new registration
            nu -c $"plugin add ($plugin_path)"
            log_success $"    ✓ Registered"

            # Verify if requested
            if $verify {
                try {
                    let found = nu -c $"plugin list | where name =~ ($plugin_name) | length" | into int
                    if $found > 0 {
                        log_success $"    ✓ Verified"
                        {success: true, verified: true}
                    } else {
                        log_warn $"    ⚠️  Could not verify"
                        {success: true, verified: false}
                    }
                } catch {
                    log_warn $"    ⚠️  Verification failed"
                    {success: true, verified: false}
                }
            } else {
                {success: true, verified: null}
            }
        } catch {|err|
            log_error $"    ✗ Registration failed: ($err.msg)"
            {success: false, verified: false}
        }

        $reg_result
    }

    let registered = ($results | where success | length)
    let failed = ($results | where {|r| not $r.success} | length)

    print ""
    log_success $"📊 Registration Summary:"
    log_success $"  ✅ Registered: ($registered) plugins"
    if $failed > 0 {
        log_error $"  ❌ Failed: ($failed) plugins"
    }
}

# Common library functions (fallback if not available)
def log_info [msg: string] {
    print $"ℹ️  ($msg)"
}

def log_success [msg: string] {
    print $"✅ ($msg)"
}

def log_error [msg: string] {
    print $"❌ ($msg)"
}

def log_warn [msg: string] {
    print $"⚠️  ($msg)"
}

# Call main function
main
