# Universal Nushell + Plugins Uninstaller for Windows
# PowerShell script that cleanly removes Nushell and plugins installation
#
# This script:
# - Detects installation locations (user or system Program Files)
# - Removes Nushell binary and plugin binaries
# - Cleans up configuration files (with backup option)
# - Removes PATH entries from environment variables
# - Unregisters plugins from Nushell
# - Provides detailed removal report

param(
    [switch]$Help,
    [switch]$Yes,
    [switch]$KeepConfig,
    [switch]$BackupConfig,
    [switch]$System,
    [switch]$User,
    [string]$UninstallDir = "",
    [switch]$DryRun,
    [switch]$Debug
)

# Configuration
$InstallDirUser = "$env:USERPROFILE\.local\bin"
$InstallDirSystem = "$env:ProgramFiles\nushell"
$ConfigDir = "$env:APPDATA\nushell"
$BackupSuffix = "uninstall-backup-$(Get-Date -Format 'yyyyMMdd_HHmmss')"

# Logging functions
function Write-Info {
    param([string]$Message)
    Write-Host "ℹ️  $Message" -ForegroundColor Blue
}

function Write-Success {
    param([string]$Message)
    Write-Host "✅ $Message" -ForegroundColor Green
}

function Write-Warning {
    param([string]$Message)
    Write-Host "⚠️  $Message" -ForegroundColor Yellow
}

function Write-Error {
    param([string]$Message)
    Write-Host "❌ $Message" -ForegroundColor Red
}

function Write-Debug {
    param([string]$Message)
    if ($Debug) {
        Write-Host "🐛 DEBUG: $Message" -ForegroundColor Magenta
    }
}

# Usage information
function Show-Usage {
    Write-Host @"
Nushell Full Distribution Uninstaller for Windows

USAGE:
    .\uninstall.ps1 [OPTIONS]

OPTIONS:
    -Help              Show this help message
    -Yes               Non-interactive mode (assume yes to prompts)
    -KeepConfig        Keep configuration files (don't remove config directory)
    -BackupConfig      Backup configuration before removal
    -System            Remove from system location (Program Files)
                       ⚠️  Requires administrator privileges (run as admin)
    -User              Remove from user location (~\.local\bin) [default]
                       No admin required - recommended
    -UninstallDir PATH Remove from custom directory (directory must be writable)
                       Bypasses interactive prompts when supplied explicitly
                       Example: -UninstallDir "C:\Tools\nushell"
    -DryRun            Show what would be removed without actually removing
    -Debug             Enable debug output

EXAMPLES:
    # Interactive removal (from user location)
    .\uninstall.ps1

    # Non-interactive removal (from user location)
    .\uninstall.ps1 -Yes

    # Remove with config backup
    .\uninstall.ps1 -BackupConfig -Yes

    # Remove from custom directory
    .\uninstall.ps1 -UninstallDir "C:\Tools\nushell" -Yes

    # Remove system installation (requires admin - right-click and 'Run as administrator')
    .\uninstall.ps1 -System -Yes

    # Preview what would be removed
    .\uninstall.ps1 -DryRun

TROUBLESHOOTING:
    • Access denied to Program Files?
      → Default uses %USERPROFILE%\.local\bin (no admin needed)
      → Or: -UninstallDir "%USERPROFILE%\.local\bin" (explicit custom path)

    • Unsure what to do?
      → Run with -DryRun first to see what would be removed
      → Default removal is safest: .\uninstall.ps1 -Yes

"@
}

# Show usage and exit if help requested
if ($Help) {
    Show-Usage
    exit 0
}

# Determine installation directory
$Interactive = -not $Yes
$IsSystemInstall = $System

if ($UninstallDir) {
    # Custom uninstall directory provided - use it directly (bypass all checks)
    $InstallDir = $UninstallDir
    Write-Info "Using custom uninstall directory (via -UninstallDir): $InstallDir"
} elseif ($IsSystemInstall) {
    $InstallDir = $InstallDirSystem
    Write-Info "Targeting system installation: $InstallDir"

    # Check if we have admin privileges
    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
    $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

    if (-not $isAdmin -and -not $DryRun) {
        Write-Warning "System installation requires administrative privileges"
        if ($Interactive) {
            $response = Read-Host "Continue with elevated privileges? [y/N]"
            if ($response -notmatch '^[yY]([eE][sS])?$') {
                Write-Info "Uninstallation cancelled"
                exit 0
            }
        }

        # Re-run as administrator
        if (-not $isAdmin) {
            Write-Info "Re-running with elevated privileges..."
            $arguments = $MyInvocation.BoundParameters.Keys | ForEach-Object { "-$_" }
            Start-Process PowerShell.exe -Argument "-File `"$PSCommandPath`" $($arguments -join ' ')" -Verb RunAs
            exit 0
        }
    }
} else {
    $InstallDir = $InstallDirUser
    Write-Info "Targeting user installation: $InstallDir"
}

# Detection functions
function Get-NushellInstallation {
    param([string]$InstallDir)

    $foundItems = @()
    Write-Debug "Detecting Nushell installation in $InstallDir"

    # Check for nu binary
    $nuPath = Join-Path $InstallDir "nu.exe"
    if (Test-Path $nuPath) {
        $foundItems += "nu.exe"
        Write-Debug "Found nu binary: $nuPath"
    }

    # Check for plugin binaries
    $pluginPattern = Join-Path $InstallDir "nu_plugin_*.exe"
    $pluginBinaries = Get-ChildItem -Path $pluginPattern -ErrorAction SilentlyContinue
    foreach ($plugin in $pluginBinaries) {
        $foundItems += $plugin.Name
        Write-Debug "Found plugin binary: $($plugin.FullName)"
    }

    return $foundItems
}

function Get-ConfigInstallation {
    param([string]$ConfigDir)

    $foundItems = @()
    Write-Debug "Detecting configuration in $ConfigDir"

    if (Test-Path $ConfigDir) {
        # Check for main config files
        $configFiles = @("config.nu", "env.nu", "distribution_config.toml")
        foreach ($configFile in $configFiles) {
            $configPath = Join-Path $ConfigDir $configFile
            if (Test-Path $configPath) {
                $foundItems += $configFile
                Write-Debug "Found config file: $configPath"
            }
        }

        # Check for plugin registration
        $pluginFiles = @("plugin.nu", "registry.dat")
        foreach ($pluginFile in $pluginFiles) {
            $pluginPath = Join-Path $ConfigDir $pluginFile
            if (Test-Path $pluginPath) {
                $foundItems += "plugin-registry"
                Write-Debug "Found plugin registry files"
                break
            }
        }
    }

    return $foundItems
}

function Get-PathEntries {
    param([string]$InstallDir)

    Write-Debug "Detecting PATH entries for $InstallDir"
    $foundIn = @()

    # Check user PATH
    $userPath = [Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::User)
    if ($userPath -and $userPath.Contains($InstallDir)) {
        $foundIn += "User PATH"
        Write-Debug "Found in User PATH"
    }

    # Check system PATH (if we have access)
    try {
        $systemPath = [Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::Machine)
        if ($systemPath -and $systemPath.Contains($InstallDir)) {
            $foundIn += "System PATH"
            Write-Debug "Found in System PATH"
        }
    } catch {
        Write-Debug "Cannot access System PATH (insufficient privileges)"
    }

    return $foundIn
}

# Removal functions
function Remove-Binaries {
    param(
        [string]$InstallDir,
        [array]$Binaries
    )

    Write-Info "Removing binaries from $InstallDir..."
    $removedCount = 0

    foreach ($binary in $Binaries) {
        $binaryPath = Join-Path $InstallDir $binary
        if (Test-Path $binaryPath) {
            Write-Info "Removing binary: $binary"
            if (-not $DryRun) {
                try {
                    Remove-Item -Path $binaryPath -Force
                    Write-Success "Removed: $binary"
                    $removedCount++
                } catch {
                    Write-Error "Failed to remove: $binary - $($_.Exception.Message)"
                }
            } else {
                Write-Info "[DRY RUN] Would remove: $binaryPath"
                $removedCount++
            }
        }
    }

    Write-Success "Removed $removedCount binaries"
}

function Backup-Configuration {
    param([string]$ConfigDir)

    if (Test-Path $ConfigDir) {
        $backupDir = "$ConfigDir.$BackupSuffix"
        Write-Info "Backing up configuration to: $backupDir"

        if (-not $DryRun) {
            try {
                Copy-Item -Path $ConfigDir -Destination $backupDir -Recurse -Force
                Write-Success "Configuration backed up successfully"
            } catch {
                Write-Error "Failed to backup configuration: $($_.Exception.Message)"
                return $false
            }
        } else {
            Write-Info "[DRY RUN] Would backup configuration to: $backupDir"
        }
        return $true
    } else {
        Write-Info "No configuration directory to backup"
        return $true
    }
}

function Remove-Configuration {
    param(
        [string]$ConfigDir,
        [array]$ConfigFiles
    )

    if ($KeepConfig) {
        Write-Info "Keeping configuration files as requested"
        return
    }

    if ($BackupConfig) {
        Backup-Configuration $ConfigDir
    }

    if (Test-Path $ConfigDir) {
        Write-Info "Removing configuration directory: $ConfigDir"

        # Ask for confirmation if interactive and not just removing empty dir
        if ($Interactive -and $ConfigFiles.Count -gt 0) {
            $response = Read-Host "Remove configuration directory $ConfigDir? [y/N]"
            if ($response -notmatch '^[yY]([eE][sS])?$') {
                Write-Info "Keeping configuration directory"
                return
            }
        }

        if (-not $DryRun) {
            try {
                Remove-Item -Path $ConfigDir -Recurse -Force
                Write-Success "Configuration directory removed"
            } catch {
                Write-Error "Failed to remove configuration directory: $($_.Exception.Message)"
            }
        } else {
            Write-Info "[DRY RUN] Would remove configuration directory: $ConfigDir"
        }
    } else {
        Write-Info "No configuration directory found"
    }
}

function Remove-PathEntries {
    param(
        [string]$InstallDir,
        [array]$PathLocations
    )

    if ($PathLocations.Count -eq 0) {
        Write-Info "No PATH entries found"
        return
    }

    Write-Info "Removing PATH entries..."

    foreach ($location in $PathLocations) {
        Write-Info "Cleaning PATH entries from: $location"

        $target = if ($location -eq "User PATH") {
            [EnvironmentVariableTarget]::User
        } else {
            [EnvironmentVariableTarget]::Machine
        }

        if (-not $DryRun) {
            try {
                $currentPath = [Environment]::GetEnvironmentVariable("Path", $target)
                $pathEntries = $currentPath -split ';'
                $newPathEntries = $pathEntries | Where-Object { $_ -ne $InstallDir }
                $newPath = $newPathEntries -join ';'

                [Environment]::SetEnvironmentVariable("Path", $newPath, $target)
                Write-Success "Cleaned PATH entries from: $location"
            } catch {
                Write-Error "Failed to clean PATH entries from: $location - $($_.Exception.Message)"
            }
        } else {
            Write-Info "[DRY RUN] Would remove PATH entries from: $location"
        }
    }
}

function Unregister-Plugins {
    param([string]$InstallDir)

    # Only try to unregister if nu is still available somewhere
    $nuBinary = $null

    if (Get-Command nu -ErrorAction SilentlyContinue) {
        $nuBinary = (Get-Command nu).Path
    } elseif (Test-Path (Join-Path $InstallDir "nu.exe")) {
        $nuBinary = Join-Path $InstallDir "nu.exe"
    } else {
        Write-Info "Nushell not available for plugin unregistration"
        return
    }

    Write-Info "Attempting to unregister plugins..."

    if (-not $DryRun) {
        try {
            # Try to get list of registered plugins
            $registeredPlugins = & $nuBinary -c "plugin list | get name" 2>$null

            if ($registeredPlugins) {
                Write-Info "Unregistering plugins from nushell..."
                foreach ($plugin in $registeredPlugins) {
                    try {
                        & $nuBinary -c "plugin rm $plugin" 2>$null
                    } catch {
                        Write-Warning "Could not unregister plugin: $plugin"
                    }
                }
                Write-Success "Plugin unregistration completed"
            } else {
                Write-Info "No registered plugins found"
            }
        } catch {
            Write-Warning "Could not retrieve plugin list"
        }
    } else {
        Write-Info "[DRY RUN] Would attempt to unregister plugins"
    }
}

# Main uninstallation process
function Main {
    Write-Info "🗑️  Nushell Full Distribution Uninstaller"
    Write-Info "========================================"

    if ($DryRun) {
        Write-Warning "DRY RUN MODE - No files will be modified"
    }

    # Detect current installation
    Write-Info ""
    Write-Info "🔍 Detecting current installation..."

    $userBinaries = Get-NushellInstallation $InstallDirUser
    $systemBinaries = Get-NushellInstallation $InstallDirSystem
    $configFiles = Get-ConfigInstallation $ConfigDir
    $userPathEntries = Get-PathEntries $InstallDirUser
    $systemPathEntries = Get-PathEntries $InstallDirSystem

    # Determine what we're removing based on target
    if ($IsSystemInstall) {
        $binaries = $systemBinaries
        $pathEntries = $systemPathEntries
    } else {
        $binaries = $userBinaries
        $pathEntries = $userPathEntries
    }

    # Show detection results
    Write-Info "Installation Status:"
    if ($userBinaries.Count -gt 0) {
        Write-Info "  📁 User binaries ($InstallDirUser): $($userBinaries -join ', ')"
    } else {
        Write-Info "  📁 User binaries ($InstallDirUser): none found"
    }

    if ($systemBinaries.Count -gt 0) {
        Write-Info "  📁 System binaries ($InstallDirSystem): $($systemBinaries -join ', ')"
    } else {
        Write-Info "  📁 System binaries ($InstallDirSystem): none found"
    }

    if ($configFiles.Count -gt 0) {
        Write-Info "  ⚙️  Configuration ($ConfigDir): $($configFiles -join ', ')"
    } else {
        Write-Info "  ⚙️  Configuration ($ConfigDir): none found"
    }

    if ($pathEntries.Count -gt 0) {
        Write-Info "  🛣️  PATH entries: $($pathEntries -join ', ')"
    } else {
        Write-Info "  🛣️  PATH entries: none found"
    }

    # Check if anything was found
    if ($binaries.Count -eq 0 -and $configFiles.Count -eq 0 -and $pathEntries.Count -eq 0) {
        Write-Warning "No Nushell installation detected"
        if ($Interactive) {
            $response = Read-Host "Continue anyway? [y/N]"
            if ($response -notmatch '^[yY]([eE][sS])?$') {
                Write-Info "Uninstallation cancelled"
                exit 0
            }
        } else {
            Write-Info "Nothing to remove"
            exit 0
        }
    }

    # Confirmation prompt
    if ($Interactive) {
        Write-Info ""
        Write-Warning "This will remove the detected Nushell installation components."
        if ($KeepConfig) {
            Write-Info "Configuration files will be kept as requested."
        } elseif ($BackupConfig) {
            Write-Info "Configuration files will be backed up before removal."
        }

        $response = Read-Host "Proceed with uninstallation? [y/N]"
        if ($response -notmatch '^[yY]([eE][sS])?$') {
            Write-Info "Uninstallation cancelled"
            exit 0
        }
    }

    # Perform uninstallation
    Write-Info ""
    Write-Info "🗑️  Starting uninstallation..."

    # Unregister plugins before removing binaries
    if ($binaries.Count -gt 0) {
        Unregister-Plugins $InstallDir
    }

    # Remove binaries
    if ($binaries.Count -gt 0) {
        Remove-Binaries $InstallDir $binaries
    }

    # Remove configuration
    if ($configFiles.Count -gt 0) {
        Remove-Configuration $ConfigDir $configFiles
    }

    # Clean PATH entries
    if ($pathEntries.Count -gt 0) {
        Remove-PathEntries $InstallDir $pathEntries
    }

    # Final summary
    Write-Info ""
    Write-Success "🎉 Uninstallation completed!"
    Write-Info "Summary:"
    Write-Info "  🗑️  Removed from: $InstallDir"
    if ($configFiles.Count -gt 0 -and -not $KeepConfig) {
        Write-Info "  🗑️  Configuration removed: $ConfigDir"
    } elseif ($KeepConfig) {
        Write-Info "  💾 Configuration kept: $ConfigDir"
    }
    if ($BackupConfig) {
        Write-Info "  💾 Configuration backed up with suffix: .$BackupSuffix"
    }

    Write-Info ""
    Write-Info "🔄 To complete removal:"
    Write-Info "1. Restart your terminal or refresh environment variables"
    Write-Info "2. Verify removal: Get-Command nu (should return nothing)"

    if ($KeepConfig -or $BackupConfig) {
        Write-Info ""
        Write-Info "📝 Note: Configuration files were preserved as requested"
    }
}

# Run main function
Main