#!/usr/bin/env nu

# Check Upstream Changes Script
# Automatically checks for upstream changes and marks plugins as OK when only nu_* dependencies differ

use lib/cargo_toml_diff.nu *

# 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 plugin registry
def load_registry [] {
    let registry_path = "etc/plugin_registry.toml"
    if not ($registry_path | path exists) {
        error make {msg: "Plugin registry not found. Please run from repository root directory."}
    }
    open $registry_path
}

# Load exclusions configuration
def load_exclusions [] {
    let exclude_path = "etc/upstream_exclude.toml"
    if ($exclude_path | path exists) {
        let config = open $exclude_path
        {
            all: ($config.exclude.plugins? | default []),
            check: ($config.exclude.check.plugins? | default []),
            merge: ($config.exclude.merge.plugins? | default []),
            patterns: ($config.exclude.patterns.plugins? | default [])
        }
    } else {
        {all: [], check: [], merge: [], patterns: []}
    }
}

# Check if plugin should be excluded
def is_plugin_excluded [plugin_name: string, operation: string, exclusions: record] {
    # Check direct exclusions
    if $plugin_name in $exclusions.all {
        return true
    }

    # Check operation-specific exclusions
    let op_exclusions = match $operation {
        "check" => $exclusions.check,
        "merge" => $exclusions.merge,
        _ => []
    }

    if $plugin_name in $op_exclusions {
        return true
    }

    # Check pattern exclusions
    for pattern in $exclusions.patterns {
        if ($plugin_name | str contains ($pattern | str replace "*" "")) {
            return true
        }
    }

    false
}

# Save plugin registry
def save_registry [registry: record] {
    $registry | to toml | save -f "etc/plugin_registry.toml"
}

# Get current date in ISO format
def current_date [] {
    date now | format date "%Y-%m-%d %H:%M:%S"
}

# Add or update git remote for upstream
def setup_upstream_remote [plugin_path: string, upstream_url: string] {
    if ($upstream_url | is-empty) {
        return
    }

    cd $plugin_path

    # Check if upstream remote exists
    let remotes = git remote | lines
    if not ("upstream" in $remotes) {
        print $"Adding upstream remote for ($plugin_path)..."
        git remote add upstream $upstream_url
    } else {
        # Update upstream URL if it changed
        let current_url = git remote get-url upstream | str trim
        if $current_url != $upstream_url {
            print $"Updating upstream remote URL for ($plugin_path)..."
            git remote set-url upstream $upstream_url
        }
    }
}

# Fetch upstream changes without merging
def fetch_upstream [plugin_path: string, upstream_branch: string] {
    cd $plugin_path

    try {
        print $"Fetching upstream changes for ($plugin_path)..."
        git fetch upstream $upstream_branch
        true
    } catch {
        print $"Failed to fetch upstream for ($plugin_path)"
        false
    }
}

# Get latest upstream commit hash
def get_upstream_commit [plugin_path: string, upstream_branch: string] {
    cd $plugin_path

    try {
        git rev-parse $"upstream/($upstream_branch)" | str trim
    } catch {
        ""
    }
}

# Check if there are differences between local and upstream
def has_upstream_changes [plugin_path: string, upstream_branch: string] {
    cd $plugin_path

    try {
        let diff_output = git diff $"HEAD..upstream/($upstream_branch)" --name-only | lines
        ($diff_output | length) > 0
    } catch {
        false
    }
}

# Create temporary upstream version of Cargo.toml for comparison
def create_upstream_cargo_toml [plugin_path: string, upstream_branch: string] {
    cd $plugin_path

    let temp_file = "Cargo.toml.upstream"
    try {
        git show $"upstream/($upstream_branch):Cargo.toml" | save $temp_file
        $temp_file
    } catch {
        ""
    }
}

# Check for source code changes (non-Cargo.toml)
def has_source_changes [plugin_path: string, upstream_branch: string] {
    cd $plugin_path

    try {
        let changed_files = git diff $"HEAD..upstream/($upstream_branch)" --name-only | lines
        let source_changes = $changed_files | where ($it =~ "\\.rs$") or ($it == "README.md") or ($it == "LICENSE")
        ($source_changes | length) > 0
    } catch {
        false
    }
}

# Process a single plugin
def process_plugin [plugin_name: string, plugin_config: record, nu_managed_deps: list<string>, exclusions: record] {
    print $"\n🔍 Checking ($plugin_name)..."

    # Check if plugin is excluded
    if (is_plugin_excluded $plugin_name "check" $exclusions) {
        print $"  🚫 Skipping ($plugin_name) - excluded from upstream checking"
        return $plugin_config
    }

    let plugin_path = $plugin_config.local_path

    # Skip if no upstream URL
    if ($plugin_config.upstream_url | is-empty) {
        print $"  ⏭️  Skipping ($plugin_name) - no upstream URL"
        return $plugin_config
    }

    # Check if plugin directory exists
    if not ($plugin_path | path exists) {
        print $"  ❌ Plugin directory not found: ($plugin_path)"
        return ($plugin_config | upsert status "error")
    }

    # Setup upstream remote
    setup_upstream_remote $plugin_path $plugin_config.upstream_url

    # Fetch upstream changes
    if not (fetch_upstream $plugin_path $plugin_config.upstream_branch) {
        print $"  ❌ Failed to fetch upstream changes"
        return ($plugin_config | upsert status "error")
    }

    # Get latest upstream commit
    let upstream_commit = get_upstream_commit $plugin_path $plugin_config.upstream_branch
    if ($upstream_commit | is-empty) {
        print $"  ❌ Could not get upstream commit hash"
        return ($plugin_config | upsert status "error")
    }

    # Check if commit changed since last check
    if $plugin_config.last_checked_commit == $upstream_commit {
        print $"  ✅ No new upstream changes since last check"
        return ($plugin_config | upsert last_checked_date (current_date))
    }

    # Check if there are any changes
    if not (has_upstream_changes $plugin_path $plugin_config.upstream_branch) {
        print $"  ✅ Local is up to date with upstream"
        return ($plugin_config
               | upsert status "ok"
               | upsert last_checked_commit $upstream_commit
               | upsert last_checked_date (current_date))
    }

    print $"  📋 Changes detected, analyzing..."

    # Check for source code changes
    let has_source = has_source_changes $plugin_path $plugin_config.upstream_branch
    if $has_source {
        print $"  ⚠️  Source code changes detected - requires manual review"
        return ($plugin_config
               | upsert status "pending"
               | upsert last_checked_commit $upstream_commit
               | upsert last_checked_date (current_date))
    }

    # Create upstream Cargo.toml for comparison
    let upstream_cargo_path = create_upstream_cargo_toml $plugin_path $plugin_config.upstream_branch
    if ($upstream_cargo_path | is-empty) {
        print $"  ❌ Could not extract upstream Cargo.toml"
        return ($plugin_config | upsert status "error")
    }

    # Compare Cargo.toml files
    let local_cargo = $"($plugin_path)/Cargo.toml"
    try {
        let diff_result = compare_cargo_tomls $local_cargo $"($plugin_path)/($upstream_cargo_path)"

        # Clean up temporary file
        rm $"($plugin_path)/($upstream_cargo_path)"

        if not $diff_result.analysis.has_changes {
            print $"  ✅ No meaningful changes in Cargo.toml"
            return ($plugin_config
                   | upsert status "ok"
                   | upsert last_checked_commit $upstream_commit
                   | upsert last_checked_date (current_date))
        }

        # Check if safe for auto-OK
        if ($plugin_config.auto_ok_on_nu_deps_only) and (is_safe_for_auto_ok $diff_result $nu_managed_deps) {
            print $"  ✅ AUTO-OK: Only nu_* dependency changes detected"
            let summary = generate_diff_summary $diff_result
            print $"      Changes:\n($summary | str replace --all '\n' '\n      ')"

            return ($plugin_config
                   | upsert status "ok"
                   | upsert last_checked_commit $upstream_commit
                   | upsert last_checked_date (current_date))
        } else {
            print $"  ⚠️  PENDING: Non-nu dependency changes require manual review"
            let summary = generate_diff_summary $diff_result
            print $"      Changes:\n($summary | str replace --all '\n' '\n      ')"

            return ($plugin_config
                   | upsert status "pending"
                   | upsert last_checked_commit $upstream_commit
                   | upsert last_checked_date (current_date))
        }
    } catch {|err|
        print $"  ❌ Error comparing Cargo.toml files: ($err.msg)"
        # Clean up on error
        try { rm $"($plugin_path)/($upstream_cargo_path)" } catch { }
        return ($plugin_config | upsert status "error")
    }
}

# Main function
def main [
    --plugin (-p): string = ""  # Check specific plugin only
    --verbose (-v)              # Verbose output
] {
    # Mandatory version check before any plugin operations
    version_check

    print "🚀 Checking upstream changes for nushell plugins..."

    # Ensure we're in the repository root directory
    if not ("nu_plugin_clipboard" | path exists) {
        error make {msg: "Please run this script from the nushell-plugins repository root directory"}
    }

    # Load registry and exclusions
    let registry = load_registry
    let exclusions = load_exclusions

    # Get managed nu dependencies
    let nu_managed_deps = $registry.settings.nu_managed_dependencies

    # Process plugins
    mut updated_registry = $registry

    let plugins_to_check = if ($plugin | is-empty) {
        $registry.plugins | transpose key value
    } else {
        if $plugin in ($registry.plugins | columns) {
            [{key: $plugin, value: ($registry.plugins | get $plugin)}]
        } else {
            error make {msg: $"Plugin '($plugin)' not found in registry"}
        }
    }

    for plugin_entry in $plugins_to_check {
        let plugin_name = $plugin_entry.key
        let plugin_config = $plugin_entry.value

        let updated_config = process_plugin $plugin_name $plugin_config $nu_managed_deps $exclusions
        $updated_registry = ($updated_registry | upsert $"plugins.($plugin_name)" $updated_config)
    }

    # Save updated registry
    save_registry $updated_registry

    print "\n📊 Summary:"
    let status_counts = $updated_registry.plugins
    | values
    | group-by status
    | transpose status count
    | each {|entry| {status: $entry.status, count: ($entry.count | length)}}

    for status in $status_counts {
        let emoji = match $status.status {
            "ok" => "✅",
            "pending" => "⚠️",
            "error" => "❌",
            "local_only" => "🏠",
            "unknown" => "❓",
            _ => "📦"
        }
        print $"  ($emoji) ($status.status): ($status.count) plugins"
    }

    print $"\n🔄 Registry updated: etc/plugin_registry.toml"
    print "💡 Run 'bash scripts/run.sh plugin_status.nu' to see detailed status"
}

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