/*
    getpkg

    getpkg install <tool_name>
        - confirms getpkg is fully initialised:
            - adds a line to the user's .bashrc file to source the getpkg ~/.bashrc_getpkg script, if it doesn't exist
            - creates an empty ~/.bashrc_getpkg script if it doesn't exist
            - creates the ~/.config/getpkg directory, if it does not exist
            - creates the ~/.local/bin/getpkg directory, if it does not exist

        - checks if ~/.config/getpkg/tool_name.json file exists. If it does, it will update the tool if the version is older than the remote version. If it doesn't exist, it will install the tool.
            - removes the tool from the user's system if it is already installed, and the tool's entries in the ~/.bashrc_getpkg script
            - downloads the tool archive (tgz) from getpkg.xyz (tool_name:ARCH), where ARCH is the architecture of the user's system, and unpacks it to the new tool directory: ~/.local/bin/getpkg/<tool_name>/
            - sets the PATH to include the tool directory, by modifying the ~/.bashrc_getpkg script
            - adds an entry for autocompletion for the tool to the ~/.bashrc_getpkg script, where autocomplete just runs <tool_name> autocomplete <args>
            - creates a ~/.config/getpkg/tool_name.json file, which contains the tool's name, version (by running <tool_name> version), hash from getpkg.xyz, and architecture
            - if setup_script.sh exists, run the script

    getpkg install <tool_name>
        - installs the specified tool

    getpkg uninstall <tool_name>
        - uninstalls the specified tool
        - removes the tool from the user's system
        - removes the tool's entries in the ~/.bashrc_getpkg script
        - removes the tool's entry in the autocomplete function in the ~/.bashrc_getpkg script
        - removes the tool's entry in the ~/.config/getpkg/tool_name.json file
        - removes the tool's directory from ~/.local/bin/getpkg/

    getpkg publish <tool_name:ARCH> <folder>
        - creates a tgz archive of the folder, and uploads it to getpkg.xyz, a simple object server.
        - prints the URL and hash of the uploaded archive
        - uses the token from env variable SOS_WRITE_TOKEN to write to getpkg.xyz
        - ARCH is optional, and if not provided it's assumed that the tool is compatible with all architectures
        - ARCH should match $(uname -m), i.e. x86_64 or aarch64
        
    getpkg update
        - updates getpkg itself, and then runs install on all already installed tools

    getpkg autocomplete <args>
        - shows autocomplete for getpkg, and then exits
        - the tool list to choose from when calling install is hard coded in the autocomplete function

    getpkg version
        - prints the version of getpkg

    getpkg create <tool_name> <directory_name>
        - creates a new tool source directory in relative path <directory_name> if it doesn't exist
        - creates a setup_script.sh file in the tool source directory if it doesn't exist, that just prints a completion message and exits

    getpkg help
        - shows this help message


*/
#include "version.hpp"
#include "BashrcEditor.hpp"
#include "DropshellScriptManager.hpp"
#include "GetbinClient.hpp"
#include "MigrationManager.hpp"
#include "ServerManager.hpp"
#include "PackageMetadata.hpp"
#include "archive_tgz.hpp"
#include "hash.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <cstring>
#include <iomanip>
#include <nlohmann/json.hpp>

#include "temp_directory.hpp"

namespace {
using json = nlohmann::json;

// Clear current line and reset cursor to beginning
void clearLine() {
    std::cout << "\r\033[K" << std::flush;
}

// Clear current line and print message
void clearAndPrint(const std::string& message) {
    clearLine();
    std::cout << message << std::flush;
}

// Compare versions (returns true if v1 < v2)
bool isVersionOlder(const std::string& v1, const std::string& v2) {
    // Simple version comparison - assumes versions are in YYYY.MMDD.HHMM format
    // or semantic versioning format (e.g., 1.2.3)
    return v1 < v2;
}

// Validate tool name to prevent path traversal and other issues
bool isValidToolName(const std::string& name) {
    if (name.empty() || name.length() > 100) return false;
    
    // Check for path traversal attempts
    if (name.find("..") != std::string::npos) return false;
    if (name.find("/") != std::string::npos) return false;
    if (name.find("\\") != std::string::npos) return false;
    
    // Only allow alphanumeric, dash, underscore, and dot
    for (char c : name) {
        if (!std::isalnum(c) && c != '-' && c != '_' && c != '.') {
            return false;
        }
    }
    
    return true;
}

// Escape string for shell command
std::string shellEscape(const std::string& str) {
    std::string result;
    result.reserve(str.length() + 20);
    result += "'";
    for (char c : str) {
        if (c == '\'') {
            result += "'\\''";
        } else {
            result += c;
        }
    }
    result += "'";
    return result;
}

std::string get_arch() {
#if defined(__x86_64__) || defined(_M_X64)
    return "x86_64";
#elif defined(__aarch64__)
    return "aarch64";
#else
    return "unknown";
#endif
}

std::string get_home() {
    const char* home = getenv("HOME");
    return home ? std::string(home) : std::string("");
}

int install_tool(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: getpkg install <tool_name>" << std::endl;
        return 1;
    }
    std::string toolName = argv[2];
    
    // Validate tool name
    if (!isValidToolName(toolName)) {
        std::cerr << "Invalid tool name. Tool names can only contain letters, numbers, dash, underscore, and dot." << std::endl;
        return 1;
    }
    
    std::string arch = get_arch();
    std::string home = get_home();
    common::TempDirectory tempDir;
    std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
    std::filesystem::path binDir = std::filesystem::path(home) / ".getpkg" / toolName;
    std::filesystem::path archivePath = tempDir.path() / (toolName + ".tgz");
    
    // Initialize ServerManager and get server list
    ServerManager serverManager;
    if (!serverManager.loadConfiguration()) {
        std::cerr << "Failed to load server configuration" << std::endl;
        return 1;
    }
    
    std::vector<std::string> servers = serverManager.getServers();
    if (servers.empty()) {
        std::cerr << "No servers configured" << std::endl;
        return 1;
    }
    
    // Initialize PackageMetadataManager
    PackageMetadataManager packageManager(configDir);
    if (!packageManager.ensurePackagesDirectory()) {
        std::cerr << "Failed to create packages directory" << std::endl;
        return 1;
    }
    
    // Check if tool needs update or install
    bool isUpdate = false;
    PackageMetadata existingMetadata;
    if (packageManager.packageExists(toolName)) {
        // Tool exists, check if update needed
        existingMetadata = packageManager.loadPackageMetadata(toolName);
        if (!existingMetadata.isValid()) {
            std::cerr << "Warning: Invalid existing package metadata for " << toolName << std::endl;
        }
        
        std::string localHash = existingMetadata.hash;
        std::string localArch = existingMetadata.arch.empty() ? arch : existingMetadata.arch;
        
        // Get remote hash to compare - use multi-server GetbinClient
        GetbinClient getbin(servers);
        std::string remoteHash;
        if (getbin.getHash(toolName, localArch, remoteHash) && !remoteHash.empty()) {
            if (localHash != remoteHash) {
                std::cout << "Updating " << toolName << "..." << std::endl;
                isUpdate = true;
            } else {
                std::cout << toolName << " is already up to date." << std::endl;
                return 0;
            }
        } else {
            // If we can't get remote hash, assume update is needed
            std::cout << "Updating " << toolName << "..." << std::endl;
            isUpdate = true;
        }
    } else {
        std::cout << "Installing " << toolName << "..." << std::endl;
    }

    // Initialize directories
    std::filesystem::create_directories(configDir);
    std::filesystem::create_directories(binDir.parent_path());
    
    // Initialize bashrc
    dropshelltool::BashrcEditor bashrcEditor;
    bashrcEditor.addSourceLine();
    
    // Remove existing installation
    DropshellScriptManager scriptManager;
    scriptManager.removeToolEntry(toolName);
    if (std::filesystem::exists(binDir)) 
        std::filesystem::remove_all(binDir);
    
    // Download tool using multi-server GetbinClient - try arch-specific version first, then universal fallback
    GetbinClient getbin2(servers);
    std::string downloadArch = arch;
    std::string sourceServer;
    
    // Progress callback for downloads
    auto progressCallback = [&toolName](size_t downloaded, size_t total) -> bool {
        if (total > 0) {
            int percent = (downloaded * 100) / total;
            std::cout << "\rDownloading " << toolName << "... " << percent << "%" << std::flush;
        } else {
            std::cout << "\rDownloading " << toolName << "... " << downloaded << " bytes" << std::flush;
        }
        return true; // Continue download
    };
    
    std::cout << "Downloading " << toolName << "..." << std::flush;
    if (!getbin2.download(toolName, arch, archivePath.string(), progressCallback)) {
        // Try universal version as fallback
        clearAndPrint("Arch-specific version not found, trying universal...\n");
        if (!getbin2.download(toolName, "universal", archivePath.string(), progressCallback)) {
            std::cerr << "\rFailed to download tool archive (tried both " << arch << " and universal)." << std::endl;
            return 1;
        }
        downloadArch = "universal";
    }
    clearAndPrint("Downloading " + toolName + "... done\n");
    
    // Find which server provided the package
    if (!getbin2.findPackageServer(toolName, downloadArch, sourceServer)) {
        // Fallback to first server if we can't determine the source
        sourceServer = servers[0];
    }
    
    // Unpack tool
    std::cout << "Unpacking..." << std::flush;
    if (!common::unpack_tgz(archivePath.string(), binDir.string())) {
        std::cerr << "\rFailed to unpack tool archive." << std::endl;
        return 1;
    }
    clearAndPrint("Unpacking... done\n");
        
    // Add to PATH and autocomplete
    std::cout << "Configuring..." << std::flush;
    scriptManager.addToolEntry(toolName, binDir.string());
    scriptManager.addAutocomplete(toolName);
    clearAndPrint("Configuring... done\n");
    
    // Get tool info
    std::string hash;
    getbin2.getHash(toolName, downloadArch, hash);
    std::string version;
    std::string toolPath = binDir.string() + "/" + toolName;
    std::string versionCmd = shellEscape(toolPath) + " version";
    FILE* fp = popen(versionCmd.c_str(), "r");
    if (fp) {
        char buf[128];
        if (fgets(buf, sizeof(buf), fp)) {
            version = std::string(buf);
            // Remove trailing newline if present
            if (!version.empty() && version.back() == '\n') {
                version.pop_back();
            }
        }
        pclose(fp);
    } else {
        std::cerr << "Warning: Failed to get version for " << toolName << std::endl;
    }
    
    // Create and save enhanced package metadata
    PackageMetadata metadata(toolName, version, hash, downloadArch, sourceServer);
    if (!packageManager.savePackageMetadata(metadata)) {
        std::cerr << "Warning: Failed to save package metadata for " << toolName << std::endl;
    }
    
    // Run setup script if exists
    std::filesystem::path setupScriptPath = binDir / "setup_script.sh";
    if (std::filesystem::exists(setupScriptPath)) {
        std::cout << "Running setup script..." << std::endl;
        std::string cmd = setupScriptPath.string();
        std::system(cmd.c_str());
    }
    
    std::cout << "Installed " << toolName << " successfully." << std::endl;
    return 0;
}

int publish_tool(int argc, char* argv[]) {
    if (argc < 4) {
        std::cerr << "Usage: getpkg publish [--server <url>] <tool_name:ARCH> <folder>" << std::endl;
        std::cerr << "       getpkg publish <tool_name:ARCH> <folder>" << std::endl;
        return 1;
    }
    
    // Parse arguments for --server option
    std::string targetServer;
    std::string labeltag;
    std::string folder;
    int argIndex = 2;
    
    if (argc >= 5 && std::string(argv[2]) == "--server") {
        targetServer = argv[3];
        labeltag = argv[4];
        folder = argv[5];
        argIndex = 5;
    } else {
        labeltag = argv[2];
        folder = argv[3];
        argIndex = 3;
    }
    
    // If no ARCH is provided (no colon in labeltag), append ":universal" for cross-platform tools
    if (labeltag.find(':') == std::string::npos) {
        labeltag += ":universal";
    } else {
        // valid labels are tool_name:ARCH, where ARCH is x86_64 or aarch64 or universal
        if (labeltag.find(':') != std::string::npos) {
            std::string arch = labeltag.substr(labeltag.find(':') + 1);
            if (arch != "x86_64" && arch != "aarch64" && arch != "universal") {
                std::cerr << "Invalid architecture: " << arch << std::endl;
                return 1;
            }
        }
    }
    
    // Initialize ServerManager
    ServerManager serverManager;
    if (!serverManager.loadConfiguration()) {
        std::cerr << "Failed to load server configuration" << std::endl;
        return 1;
    }
    
    // Determine target server
    std::string publishServer;
    if (!targetServer.empty()) {
        // User specified a server, validate it exists in configuration
        std::vector<std::string> servers = serverManager.getServers();
        if (std::find(servers.begin(), servers.end(), targetServer) == servers.end()) {
            std::cerr << "Error: Server '" << targetServer << "' is not configured" << std::endl;
            std::cerr << "Use 'getpkg server add " << targetServer << "' to add it first" << std::endl;
            return 1;
        }
        publishServer = targetServer;
    } else {
        // Use default publish server (first server with write token)
        publishServer = serverManager.getDefaultPublishServer();
        if (publishServer.empty()) {
            std::cerr << "Error: No servers with write tokens configured" << std::endl;
            std::cerr << "Use 'getpkg server add <url>' and provide a write token" << std::endl;
            return 1;
        }
    }
    
    // Get write token for the target server
    std::string token = serverManager.getWriteToken(publishServer);
    if (token.empty()) {
        // Check environment variable as fallback
        const char* envToken = std::getenv("SOS_WRITE_TOKEN");
        if (envToken && std::strlen(envToken) > 0) {
            token = envToken;
        } else {
            std::cerr << "Error: No write token found for server '" << publishServer << "'" << std::endl;
            std::cerr << "Set SOS_WRITE_TOKEN environment variable or configure token for this server" << std::endl;
            return 1;
        }
    }
    
    std::string home = get_home();
    std::filesystem::path archivePath = std::filesystem::path(home) / ".tmp" / (labeltag + ".tgz");
    std::filesystem::create_directories(archivePath.parent_path());

    if (!common::pack_tgz(folder, archivePath.string())) {
        std::cerr << "Failed to create archive." << std::endl;
        return 1;
    }
    
    // Initialize GetbinClient with server list
    std::vector<std::string> servers = serverManager.getServers();
    GetbinClient getbin(servers);
    std::string url, hash;
    
    // Progress callback for upload
    auto uploadProgressCallback = [](size_t uploaded, size_t total) -> bool {
        if (total > 0) {
            int percent = (uploaded * 100) / total;
            std::cout << "\rUploading... " << percent << "%" << std::flush;
        } else {
            std::cout << "\rUploading... " << uploaded << " bytes" << std::flush;
        }
        return true; // Continue upload
    };
    
    std::cout << "Publishing to " << publishServer << "..." << std::endl;
    std::cout << "Uploading..." << std::flush;
    if (!getbin.upload(publishServer, archivePath.string(), url, hash, token, uploadProgressCallback)) {
        std::cerr << "\rFailed to upload archive to " << publishServer << std::endl;
        return 1;
    }
    clearAndPrint("Uploading... done\n");
    std::cout << "Published to " << publishServer << "! URL: " << url << "\nHash: " << hash << std::endl;
    return 0;
}

int update_tool(int argc, char* argv[]) {
    std::string home = get_home();
    std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
    
    // Initialize ServerManager and PackageMetadataManager
    ServerManager serverManager;
    if (!serverManager.loadConfiguration()) {
        std::cerr << "Failed to load server configuration" << std::endl;
        return 1;
    }
    
    std::vector<std::string> servers = serverManager.getServers();
    if (servers.empty()) {
        std::cerr << "No servers configured" << std::endl;
        return 1;
    }
    
    PackageMetadataManager packageManager(configDir);
    if (!packageManager.ensurePackagesDirectory()) {
        std::cerr << "Failed to create packages directory" << std::endl;
        return 1;
    }
    
    // Structure to hold tool information
    struct ToolInfo {
        std::string name;
        std::string localHash;
        std::string remoteHash;
        std::string arch;
        std::string version;
        std::string sourceServer;
        bool needsUpdate = false;
        std::string status = "Up to date";
    };
    
    std::vector<ToolInfo> tools;
    
    // Collect all installed tools using PackageMetadataManager
    std::vector<std::string> installedPackages = packageManager.listInstalledPackages();
    for (const std::string& toolName : installedPackages) {
        ToolInfo tool;
        tool.name = toolName;
        
        // Load package metadata
        PackageMetadata metadata = packageManager.loadPackageMetadata(toolName);
        if (metadata.isValid()) {
            tool.localHash = metadata.hash;
            tool.arch = metadata.arch.empty() ? get_arch() : metadata.arch;
            tool.version = metadata.version;
            tool.sourceServer = metadata.sourceServer;
            
            if (tool.version.empty() || tool.version == "-") {
                tool.version = "installed";
            }
        } else {
            // Fallback to legacy format if new format fails
            std::filesystem::path legacyPath = configDir / (toolName + ".json");
            if (std::filesystem::exists(legacyPath)) {
                std::ifstream tfile(legacyPath);
                if (tfile.good()) {
                    json toolInfo;
                    tfile >> toolInfo;
                    tool.localHash = toolInfo.value("hash", "");
                    tool.arch = toolInfo.value("arch", get_arch());
                    tool.version = toolInfo.value("version", "-");
                    tool.sourceServer = "getpkg.xyz"; // Default for legacy
                    
                    if (!tool.version.empty() && tool.version.back() == '\n') {
                        tool.version.pop_back();
                    }
                    if (tool.version.empty() || tool.version == "-") {
                        tool.version = "installed";
                    }
                }
            }
        }
        
        tools.push_back(tool);
    }
    
    if (tools.empty()) {
        std::cout << "No tools installed." << std::endl;
        return 0;
    }
    
    // Step 1: Check for updates (with progress)
    std::cout << "Checking " << tools.size() << " tools for updates..." << std::endl;
    
    GetbinClient getbin(servers);
    for (size_t i = 0; i < tools.size(); ++i) {
        auto& tool = tools[i];
        
        // Show progress
        std::cout << "\r[" << (i + 1) << "/" << tools.size() << "] Checking " << tool.name << "..." << std::flush;
        
        // Check remote hash - use multi-server fallback
        std::string remoteHash;
        if (getbin.getHash(tool.name, tool.arch, remoteHash) && !remoteHash.empty()) {
            tool.remoteHash = remoteHash;
            if (tool.localHash != remoteHash) {
                tool.needsUpdate = true;
                tool.status = "Needs update";
            }
        } else {
            tool.status = "Check failed";
        }
    }
    clearLine(); // Clear progress line
    
    // Step 2: Update tools that need updating
    std::vector<std::tuple<std::string, std::string, std::string>> updateResults;
    
    // First update getpkg if it needs updating
    auto getpkgIt = std::find_if(tools.begin(), tools.end(), 
                                 [](const ToolInfo& t) { return t.name == "getpkg"; });
    
    if (getpkgIt != tools.end() && getpkgIt->needsUpdate) {
        std::cout << "Updating getpkg..." << std::flush;
        
        // Use install_tool for actual update
        std::stringstream buffer, errBuffer;
        std::streambuf* oldOut = std::cout.rdbuf(buffer.rdbuf());
        std::streambuf* oldErr = std::cerr.rdbuf(errBuffer.rdbuf());
        
        char* toolArgv[] = {argv[0], (char*)"install", (char*)"getpkg"};
        int result = install_tool(3, toolArgv);
        
        std::cout.rdbuf(oldOut);
        std::cerr.rdbuf(oldErr);
        
        if (result == 0) {
            getpkgIt->status = "Updated";
            std::cout << " Updated" << std::endl;
        } else {
            getpkgIt->status = "Failed";
            std::cout << " Failed" << std::endl;
        }
    }
    
    // Update other tools
    int toolsToUpdate = std::count_if(tools.begin(), tools.end(), 
                                      [](const ToolInfo& t) { return t.needsUpdate && t.name != "getpkg"; });
    
    if (toolsToUpdate > 0) {
        std::cout << "Updating " << toolsToUpdate << " tools..." << std::endl;
        
        int updatedCount = 0;
        for (auto& tool : tools) {
            if (tool.needsUpdate && tool.name != "getpkg") {
                updatedCount++;
                std::cout << "[" << updatedCount << "/" << toolsToUpdate << "] Updating " << tool.name << "..." << std::flush;
                
                // Use install_tool for actual update
                std::stringstream buffer, errBuffer;
                std::streambuf* oldOut = std::cout.rdbuf(buffer.rdbuf());
                std::streambuf* oldErr = std::cerr.rdbuf(errBuffer.rdbuf());
                
                char* toolArgv[] = {argv[0], (char*)"install", (char*)tool.name.c_str()};
                int result = install_tool(3, toolArgv);
                
                std::cout.rdbuf(oldOut);
                std::cerr.rdbuf(oldErr);
                
                if (result == 0) {
                    tool.status = "Updated";
                    clearAndPrint("Updated\n");
                    
                    // Re-read version after update using PackageMetadataManager
                    PackageMetadata updatedMetadata = packageManager.loadPackageMetadata(tool.name);
                    if (updatedMetadata.isValid()) {
                        tool.version = updatedMetadata.version;
                        if (tool.version.empty() || tool.version == "-") {
                            tool.version = "installed";
                        }
                    }
                } else {
                    tool.status = "Failed";
                    clearAndPrint("Failed\n");
                }
            }
        }
    }
    
    // Prepare results for display
    for (const auto& tool : tools) {
        updateResults.push_back(std::make_tuple(tool.name, tool.status, tool.version));
    }
    
    // Display results in a table
    std::cout << std::endl;
    std::cout << "+" << std::string(25, '-') << "+" << std::string(15, '-') << "+" << std::string(20, '-') << "+" << std::endl;
    std::cout << "|" << std::setw(25) << std::left << " Tool" 
              << "|" << std::setw(15) << std::left << " Status" 
              << "|" << std::setw(20) << std::left << " Version"
              << "|" << std::endl;
    std::cout << "+" << std::string(25, '-') << "+" << std::string(15, '-') << "+" << std::string(20, '-') << "+" << std::endl;
    
    int updatedCount = 0;
    int upToDateCount = 0;
    int failedCount = 0;
    
    for (const auto& [name, status, version] : updateResults) {
        std::string displayStatus = status;
        if (status == "Updated") {
            updatedCount++;
        } else if (status == "Up to date") {
            upToDateCount++;
        } else {
            failedCount++;
        }
        
        std::cout << "|" << std::setw(25) << std::left << (" " + name)
                  << "|" << std::setw(15) << std::left << (" " + displayStatus)
                  << "|" << std::setw(20) << std::left << (" " + version)
                  << "|" << std::endl;
    }
    
    std::cout << "+" << std::string(25, '-') << "+" << std::string(15, '-') << "+" << std::string(20, '-') << "+" << std::endl;
    std::cout << std::endl;
    
    // Summary
    std::cout << "Update Summary: ";
    if (updatedCount > 0) {
        std::cout << updatedCount << " updated";
    }
    if (upToDateCount > 0) {
        if (updatedCount > 0) std::cout << ", ";
        std::cout << upToDateCount << " already up to date";
    }
    if (failedCount > 0) {
        if (updatedCount > 0 || upToDateCount > 0) std::cout << ", ";
        std::cout << failedCount << " failed";
    }
    std::cout << std::endl;
    
    return 0;
}

int create_tool(int argc, char* argv[]) {
    if (argc < 4) {
        std::cerr << "Usage: getpkg create <tool_name> <directory_name>" << std::endl;
        return 1;
    }
    std::string toolName = argv[2];
    std::string directoryName = argv[3];
    std::filesystem::path toolDir = std::filesystem::current_path() / directoryName;
    if (!std::filesystem::exists(toolDir)) {
        std::filesystem::create_directories(toolDir);
        std::cout << "Created directory: " << toolDir << std::endl;
    } else {
        std::cout << "Directory already exists: " << toolDir << std::endl;
    }
    std::filesystem::path setupScriptPath = toolDir / "setup_script.sh";
    if (!std::filesystem::exists(setupScriptPath)) {
        std::ofstream setupFile(setupScriptPath);
        setupFile << "#!/bin/bash\necho 'Setup complete.'\n";
        setupFile.close();
        std::filesystem::permissions(setupScriptPath, std::filesystem::perms::owner_exec | std::filesystem::perms::owner_write | std::filesystem::perms::owner_read);
        std::cout << "Created setup script: " << setupScriptPath << std::endl;
    } else {
        std::cout << "Setup script already exists: " << setupScriptPath << std::endl;
    }
    return 0;
}

int hash_command(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: getpkg hash <file_or_directory>" << std::endl;
        return 1;
    }
    std::string path = argv[2];
    
    if (!std::filesystem::exists(path)) {
        std::cerr << "Error: Path does not exist: " << path << std::endl;
        return 1;
    }
    
    uint64_t hash = common::hash_path(path);
    std::cout << hash << std::endl;
    return 0;
}

int unpublish_tool(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: getpkg unpublish [--server <url>] <tool_name[:ARCH]>" << std::endl;
        std::cerr << "       getpkg unpublish [--server <url>] <hash>" << std::endl;
        std::cerr << "       getpkg unpublish <tool_name[:ARCH]>" << std::endl;
        std::cerr << "       getpkg unpublish <hash>" << std::endl;
        return 1;
    }
    
    // Parse arguments for --server option
    std::string targetServer;
    std::string target;
    
    if (argc >= 4 && std::string(argv[2]) == "--server") {
        if (argc < 5) {
            std::cerr << "Usage: getpkg unpublish --server <url> <tool_name[:ARCH]|hash>" << std::endl;
            return 1;
        }
        targetServer = argv[3];
        target = argv[4];
    } else {
        target = argv[2];
    }
    
    // Initialize ServerManager
    ServerManager serverManager;
    if (!serverManager.loadConfiguration()) {
        std::cerr << "Failed to load server configuration" << std::endl;
        return 1;
    }
    
    // Determine target server
    std::string unpublishServer;
    if (!targetServer.empty()) {
        // User specified a server, validate it exists in configuration
        std::vector<std::string> servers = serverManager.getServers();
        if (std::find(servers.begin(), servers.end(), targetServer) == servers.end()) {
            std::cerr << "Error: Server '" << targetServer << "' is not configured" << std::endl;
            std::cerr << "Use 'getpkg server add " << targetServer << "' to add it first" << std::endl;
            return 1;
        }
        unpublishServer = targetServer;
    } else {
        // Use default publish server (first server with write token)
        unpublishServer = serverManager.getDefaultPublishServer();
        if (unpublishServer.empty()) {
            std::cerr << "Error: No servers with write tokens configured" << std::endl;
            std::cerr << "Use 'getpkg server add <url>' and provide a write token" << std::endl;
            return 1;
        }
    }
    
    // Get write token for the target server
    std::string token = serverManager.getWriteToken(unpublishServer);
    if (token.empty()) {
        // Check environment variable as fallback
        const char* envToken = std::getenv("SOS_WRITE_TOKEN");
        if (envToken && std::strlen(envToken) > 0) {
            token = envToken;
        } else {
            std::cerr << "Error: No write token found for server '" << unpublishServer << "'" << std::endl;
            std::cerr << "Set SOS_WRITE_TOKEN environment variable or configure token for this server" << std::endl;
            return 1;
        }
    }
    
    // Initialize GetbinClient with server list
    std::vector<std::string> servers = serverManager.getServers();
    GetbinClient getbin(servers);
    std::string hash = target;
    
    // Check if target looks like a hash (all digits) or a tool name
    bool isHash = true;
    for (char c : target) {
        if (!std::isdigit(c)) {
            isHash = false;
            break;
        }
    }
    
    if (!isHash) {
        // Target is a tool name, need to get hashes for all architectures
        std::string toolName = target;
        std::string specificArch = "";
        
        // Check if arch is specified (contains :)
        if (target.find(':') != std::string::npos) {
            toolName = target.substr(0, target.find(':'));
            specificArch = target.substr(target.find(':') + 1);
        }
        
        // If a specific architecture was requested, only unpublish that one
        if (!specificArch.empty()) {
            if (!getbin.getHash(unpublishServer, toolName, specificArch, hash)) {
                std::cerr << "Failed to get hash for " << target << " on server " << unpublishServer << std::endl;
                return 1;
            }
            
            if (hash.empty()) {
                std::cerr << "Tool " << target << " not found" << std::endl;
                return 1;
            }
            
            // Validate that we got a real hash (should be numeric)
            bool validHash = true;
            for (char c : hash) {
                if (!std::isdigit(c)) {
                    validHash = false;
                    break;
                }
            }
            
            if (!validHash) {
                std::cerr << "Error: Invalid hash received: " << hash << std::endl;
                std::cerr << "The server may not have found the package." << std::endl;
                return 1;
            }
            
            std::cout << "Found hash " << hash << " for " << target << " on " << unpublishServer << std::endl;
            
            // Delete the specific architecture
            if (getbin.deleteObject(hash, token)) {
                std::cout << "Successfully unpublished " << target << " from " << unpublishServer << " (hash: " << hash << ")" << std::endl;
                return 0;
            } else {
                std::cerr << "Failed to unpublish " << target << " from " << unpublishServer << std::endl;
                return 1;
            }
        } else {
            // No specific architecture - unpublish ALL entries with this tool name
            std::vector<std::pair<std::string, std::vector<std::string>>> allEntries;
            std::vector<std::pair<std::string, std::string>> foundPackages; // (tag, hash)
            
            std::cout << "Searching for all entries with label '" << toolName << "'..." << std::endl;
            
            if (!getbin.listAllEntries(allEntries)) {
                std::cerr << "Failed to get directory listing from server" << std::endl;
                return 1;
            }
            
            // Find all entries with labeltags starting with toolName:
            for (const auto& entry : allEntries) {
                const std::string& hash = entry.first;
                const std::vector<std::string>& labeltags = entry.second;
                
                for (const std::string& tag : labeltags) {
                    if (tag.find(toolName + ":") == 0) {
                        // Found a matching labeltag
                        foundPackages.push_back({tag, hash});
                        std::cout << "  Found " << tag << " (hash: " << hash << ")" << std::endl;
                        break; // Only count each hash once even if it has multiple matching tags
                    }
                }
            }
            
            if (foundPackages.empty()) {
                std::cerr << "No packages found for " << toolName << std::endl;
                return 1;
            }
            
            // Unpublish all found packages
            std::cout << std::endl;
            std::cout << "Unpublishing " << foundPackages.size() << " package(s)..." << std::endl;
            
            int successCount = 0;
            int failCount = 0;
            
            for (const auto& [arch, archHash] : foundPackages) {
                std::cout << "  Unpublishing " << arch << "... ";
                if (getbin.deleteObject(archHash, token)) {
                    std::cout << "OK" << std::endl;
                    successCount++;
                } else {
                    std::cout << "FAILED" << std::endl;
                    failCount++;
                }
            }
            
            std::cout << std::endl;
            if (failCount == 0) {
                std::cout << "Successfully unpublished all " << successCount << " version(s) of " << toolName << std::endl;
                return 0;
            } else {
                std::cerr << "Failed to unpublish " << failCount << " out of " << foundPackages.size() << " packages" << std::endl;
                return 1;
            }
        }
    } else {
        // Target is a hash - just delete it directly
        if (getbin.deleteObject(hash, token)) {
            std::cout << "Successfully unpublished hash " << hash << std::endl;
            return 0;
        } else {
            std::cerr << "Failed to unpublish hash " << hash << std::endl;
            return 1;
        }
    }
}

int list_packages(int argc, char* argv[]) {
    GetbinClient getbin;
    std::vector<std::string> availablePackages;
    
    std::cout << "Fetching package list from getpkg.xyz..." << std::endl;
    if (!getbin.listPackages(availablePackages)) {
        std::cerr << "Failed to fetch package list from server." << std::endl;
        return 1;
    }
    
    if (availablePackages.empty()) {
        std::cout << "No packages found on server." << std::endl;
        return 0;
    }
    
    // Get local installation status
    std::string home = get_home();
    std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
    std::filesystem::path getpkgDir = std::filesystem::path(home) / ".getpkg";
    
    // Build list of installed packages with their info
    std::map<std::string, json> installedPackages;
    if (std::filesystem::exists(configDir)) {
        for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
            if (entry.path().extension() == ".json") {
                std::string toolName = entry.path().stem().string();
                try {
                    std::ifstream configFile(entry.path());
                    json toolConfig;
                    configFile >> toolConfig;
                    installedPackages[toolName] = toolConfig;
                } catch (...) {
                    // Skip malformed config files
                }
            }
        }
    }
    
    // Print header
    std::cout << std::endl;
    std::cout << "+" << std::string(25, '-') << "+" << std::string(12, '-') << "+" << std::string(18, '-') << "+" << std::string(10, '-') << "+" << std::endl;
    std::cout << "|" << std::setw(25) << std::left << " Package" 
              << "|" << std::setw(12) << std::left << " Status" 
              << "|" << std::setw(18) << std::left << " Local Version"
              << "|" << std::setw(10) << std::left << " Latest"
              << "|" << std::endl;
    std::cout << "+" << std::string(25, '-') << "+" << std::string(12, '-') << "+" << std::string(18, '-') << "+" << std::string(10, '-') << "+" << std::endl;
    
    // Process each package
    for (const auto& packageName : availablePackages) {
        std::string status = "Available";
        std::string localVersion = "-";
        std::string remoteStatus = "-";
        
        auto it = installedPackages.find(packageName);
        if (it != installedPackages.end()) {
            // Package is installed
            json& config = it->second;
            localVersion = config.value("version", "unknown");
            std::string localHash = config.value("hash", "");
            std::string arch = config.value("arch", get_arch());
            
            // Check if it's up to date by comparing hashes
            std::string remoteHash;
            if (getbin.getHash(packageName, arch, remoteHash) && !remoteHash.empty()) {
                if (localHash == remoteHash) {
                    status = "Up to date";
                    remoteStatus = "OK";
                } else {
                    status = "Update";
                    remoteStatus = "Update";
                }
            } else {
                status = "Local";
                remoteStatus = "?";
            }
        }
        
        // Print row
        std::cout << "|" << std::setw(25) << std::left << (" " + packageName).substr(0, 24)
                  << "|" << std::setw(12) << std::left << (" " + status).substr(0, 11)
                  << "|" << std::setw(18) << std::left << (" " + localVersion).substr(0, 17)
                  << "|" << std::setw(10) << std::left << (" " + remoteStatus).substr(0, 9)
                  << "|" << std::endl;
    }
    
    // Print footer
    std::cout << "+" << std::string(25, '-') << "+" << std::string(12, '-') << "+" << std::string(18, '-') << "+" << std::string(10, '-') << "+" << std::endl;
    std::cout << std::endl;
    std::cout << "Total packages: " << availablePackages.size() 
              << ", Installed: " << installedPackages.size() << std::endl;
    
    return 0;
}

int clean_tool(int argc, char* argv[]) {
    std::string home = get_home();
    std::filesystem::path getpkgDir = std::filesystem::path(home) / ".getpkg";
    std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
    std::filesystem::path binDir = std::filesystem::path(home) / ".local/bin/getpkg";
    
    std::cout << "Cleaning up getpkg installation..." << std::endl;
    
    // Step 1: Build list of installed tools from ~/.getpkg/
    std::set<std::string> installedTools;
    if (std::filesystem::exists(getpkgDir)) {
        for (const auto& entry : std::filesystem::directory_iterator(getpkgDir)) {
            if (entry.is_directory()) {
                installedTools.insert(entry.path().filename().string());
            }
        }
    }
    
    std::cout << "Found " << installedTools.size() << " installed tools." << std::endl;
    
    // Step 2: Clean up ~/.config/getpkg/ - remove files for non-existent tools
    int configFilesRemoved = 0;
    if (std::filesystem::exists(configDir)) {
        for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
            if (entry.path().extension() == ".json") {
                std::string toolName = entry.path().stem().string();
                if (installedTools.find(toolName) == installedTools.end()) {
                    std::cout << "Removing orphaned config: " << entry.path().filename() << std::endl;
                    std::filesystem::remove(entry.path());
                    configFilesRemoved++;
                }
            }
        }
    }
    
    // Step 3: Clean up ~/.local/bin/getpkg/ - remove dangling symlinks and non-symlinks
    int danglingSymlinksRemoved = 0;
    int nonSymlinksRemoved = 0;
    if (std::filesystem::exists(binDir)) {
        for (const auto& entry : std::filesystem::directory_iterator(binDir)) {
            if (entry.is_symlink()) {
                try {
                    std::filesystem::path target = std::filesystem::read_symlink(entry.path());
                    // Check if the symlink target exists
                    if (!std::filesystem::exists(target)) {
                        std::cout << "Removing dangling symlink: " << entry.path().filename() << std::endl;
                        std::filesystem::remove(entry.path());
                        danglingSymlinksRemoved++;
                    }
                } catch (const std::exception& e) {
                    std::cout << "Removing invalid symlink: " << entry.path().filename() << " (" << e.what() << ")" << std::endl;
                    std::filesystem::remove(entry.path());
                    danglingSymlinksRemoved++;
                }
            } else {
                // Remove anything that's not a symlink (directories, regular files, etc.)
                std::cout << "Removing non-symlink: " << entry.path().filename() << std::endl;
                if (entry.is_directory()) {
                    std::filesystem::remove_all(entry.path());
                } else {
                    std::filesystem::remove(entry.path());
                }
                nonSymlinksRemoved++;
            }
        }
    }
    
    // Step 4: Clean up autocomplete entries and obsolete PATH exports in ~/.bashrc_getpkg
    DropshellScriptManager scriptManager;
    int autocompleteCleaned = 0;
    int pathEntriesRemoved = 0;
    std::filesystem::path bashrcPath = std::filesystem::path(home) / ".bashrc_getpkg";
    if (std::filesystem::exists(bashrcPath)) {
        std::ifstream infile(bashrcPath);
        std::vector<std::string> lines;
        std::string line;
        bool inAutocompleteBlock = false;
        std::string currentTool = "";
        
        while (std::getline(infile, line)) {
            // Check for obsolete PATH exports (we use symlinks now, not PATH modifications)
            if (line.find("export PATH=") != std::string::npos && line.find("/.local/bin/getpkg/") != std::string::npos) {
                // Extract tool name from the PATH line
                size_t toolStart = line.find("/.local/bin/getpkg/");
                if (toolStart != std::string::npos) {
                    toolStart += strlen("/.local/bin/getpkg/");
                    size_t toolEnd = line.find(":", toolStart);
                    if (toolEnd != std::string::npos) {
                        std::string toolInPath = line.substr(toolStart, toolEnd - toolStart);
                        std::cout << "Removing obsolete PATH entry for: " << toolInPath << std::endl;
                        pathEntriesRemoved++;
                        continue; // Skip this line
                    }
                }
            }
            
            // Check for autocomplete block start
            if (line.find("# GETPKG-AUTOCOMPLETE-START: ") != std::string::npos) {
                currentTool = line.substr(line.find(": ") + 2);
                inAutocompleteBlock = true;
                
                // Check if this tool still exists
                if (installedTools.find(currentTool) == installedTools.end()) {
                    std::cout << "Removing autocomplete for removed tool: " << currentTool << std::endl;
                    autocompleteCleaned++;
                    // Skip this block entirely
                    continue;
                }
            }
            
            // Check for autocomplete block end
            if (line.find("# GETPKG-AUTOCOMPLETE-END: ") != std::string::npos) {
                if (inAutocompleteBlock && installedTools.find(currentTool) != installedTools.end()) {
                    lines.push_back(line); // Keep the end marker if we kept the block
                }
                inAutocompleteBlock = false;
                currentTool = "";
                continue;
            }
            
            // If we're in a block for a tool that still exists, keep the line
            if (inAutocompleteBlock) {
                if (installedTools.find(currentTool) != installedTools.end()) {
                    lines.push_back(line);
                }
                // Otherwise skip (we're removing this block)
            } else {
                lines.push_back(line);
            }
        }
        infile.close();
        
        // Write back the cleaned file if we removed anything
        if (autocompleteCleaned > 0 || pathEntriesRemoved > 0) {
            std::ofstream outfile(bashrcPath, std::ios::trunc);
            for (const auto& l : lines) {
                outfile << l << "\n";
            }
            outfile.close();
        }
    }
    
    // Step 5: Remove empty directories in ~/.getpkg/
    int emptyDirsRemoved = 0;
    if (std::filesystem::exists(getpkgDir)) {
        for (const auto& entry : std::filesystem::directory_iterator(getpkgDir)) {
            if (entry.is_directory() && std::filesystem::is_empty(entry.path())) {
                std::cout << "Removing empty directory: " << entry.path().filename() << std::endl;
                std::filesystem::remove(entry.path());
                emptyDirsRemoved++;
            }
        }
    }
    
    // Summary
    std::cout << "\nCleanup complete:" << std::endl;
    std::cout << "  - Removed " << configFilesRemoved << " orphaned config files" << std::endl;
    std::cout << "  - Removed " << danglingSymlinksRemoved << " dangling symlinks" << std::endl;
    std::cout << "  - Removed " << nonSymlinksRemoved << " non-symlinks from ~/.local/bin/getpkg" << std::endl;
    std::cout << "  - Cleaned " << autocompleteCleaned << " autocomplete entries" << std::endl;
    std::cout << "  - Removed " << pathEntriesRemoved << " obsolete PATH entries" << std::endl;
    std::cout << "  - Removed " << emptyDirsRemoved << " empty directories" << std::endl;
    
    return 0;
}

int uninstall_tool(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: getpkg uninstall <tool_name>" << std::endl;
        return 1;
    }
    std::string toolName = argv[2];
    
    // Validate tool name
    if (!isValidToolName(toolName)) {
        std::cerr << "Invalid tool name. Tool names can only contain letters, numbers, dash, underscore, and dot." << std::endl;
        return 1;
    }
    
    std::string home = get_home();
    std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
    std::filesystem::path binDir = std::filesystem::path(home) / ".getpkg" / toolName;
    std::filesystem::path toolInfoPath = configDir / (toolName + ".json");
    
    // Check if tool is installed
    if (!std::filesystem::exists(toolInfoPath)) {
        std::cerr << "Tool " << toolName << " is not installed." << std::endl;
        return 1;
    }
    
    std::cout << "Uninstalling " << toolName << "..." << std::endl;
    
    // Remove tool entries from bashrc_getpkg
    DropshellScriptManager scriptManager;
    scriptManager.removeToolEntry(toolName);
    scriptManager.removeAutocomplete(toolName);
    
    // Remove tool directory
    if (std::filesystem::exists(binDir)) {
        std::filesystem::remove_all(binDir);
    }
    
    // Remove tool info file
    if (std::filesystem::exists(toolInfoPath)) {
        std::filesystem::remove(toolInfoPath);
    }
    
    std::cout << "Uninstalled " << toolName << " successfully." << std::endl;
    return 0;
}

void show_help() {
    std::cout << "getpkg - Package manager for dropshell tools" << std::endl;
    std::cout << "Version: " << dropshell::VERSION << std::endl;
    std::cout << std::endl;
    std::cout << "USAGE:" << std::endl;
    std::cout << "  getpkg <command> [args...]            Run a specific command" << std::endl;
    std::cout << std::endl;
    std::cout << "COMMANDS:" << std::endl;
    std::cout << "  install <tool_name>                   Install or update a tool" << std::endl;
    std::cout << "      Downloads and installs the tool from getpkg.xyz" << std::endl;
    std::cout << "      Updates if a newer version is available" << std::endl;
    std::cout << std::endl;
    std::cout << "  uninstall <tool_name>                 Remove an installed tool" << std::endl;
    std::cout << "      Removes tool files, PATH entries, and autocomplete" << std::endl;
    std::cout << std::endl;
    std::cout << "  publish [--server <url>] <tool_name[:ARCH]> <folder>" << std::endl;
    std::cout << "      Upload a tool to a package server" << std::endl;
    std::cout << "      ARCH is optional (defaults to 'universal')" << std::endl;
    std::cout << "      Uses default publish server if --server not specified" << std::endl;
    std::cout << std::endl;
    std::cout << "  unpublish [--server <url>] <tool_name>         Remove ALL architectures of a tool" << std::endl;
    std::cout << "  unpublish [--server <url>] <tool_name:ARCH>    Remove specific architecture only" << std::endl;
    std::cout << "  unpublish [--server <url>] <hash>              Remove a tool by hash" << std::endl;
    std::cout << "      Uses default publish server if --server not specified" << std::endl;
    std::cout << "      Without :ARCH, removes x86_64, aarch64, and universal versions" << std::endl;
    std::cout << std::endl;
    std::cout << "  update                                Update getpkg and all installed tools" << std::endl;
    std::cout << std::endl;
    std::cout << "  create <tool_name> <directory>        Create a new tool project" << std::endl;
    std::cout << "      Creates directory structure and setup script" << std::endl;
    std::cout << std::endl;
    std::cout << "  hash <file_or_directory>              Calculate hash of file or directory" << std::endl;
    std::cout << "      Outputs raw hash value to stdout" << std::endl;
    std::cout << std::endl;
    std::cout << "  list                                  List all available packages" << std::endl;
    std::cout << "      Shows installation status and version information" << std::endl;
    std::cout << std::endl;
    std::cout << "  clean                                 Clean up orphaned configs and symlinks" << std::endl;
    std::cout << "      Removes unused config files and dangling symlinks" << std::endl;
    std::cout << std::endl;
    std::cout << "  server add <url>                      Add a new package server" << std::endl;
    std::cout << "      Adds a server to the configuration for package discovery" << std::endl;
    std::cout << std::endl;
    std::cout << "  server remove <url>                   Remove a package server" << std::endl;
    std::cout << "      Removes a server from the configuration" << std::endl;
    std::cout << std::endl;
    std::cout << "  server list                           List all configured servers" << std::endl;
    std::cout << "      Shows all servers with their status and write token info" << std::endl;
    std::cout << std::endl;
    std::cout << "  version                               Show getpkg version" << std::endl;
    std::cout << std::endl;
    std::cout << "  help                                  Show this help message" << std::endl;
    std::cout << std::endl;
    std::cout << "EXAMPLES:" << std::endl;
    std::cout << "  getpkg install whatsdirty                Install whatsdirty tool" << std::endl;
    std::cout << "  getpkg install myapp                  Install myapp" << std::endl;
    std::cout << "  getpkg publish myapp:x86_64 ./build   Publish architecture-specific build" << std::endl;
    std::cout << "  getpkg publish myapp ./build          Publish universal build" << std::endl;
    std::cout << "  getpkg publish --server example.com myapp ./build  Publish to specific server" << std::endl;
    std::cout << "  getpkg unpublish myapp                Remove ALL architectures of myapp" << std::endl;
    std::cout << "  getpkg unpublish myapp:x86_64         Remove only x86_64 version" << std::endl;
    std::cout << "  getpkg unpublish --server example.com myapp  Remove from specific server" << std::endl;
    std::cout << "  getpkg uninstall myapp                Remove myapp from system" << std::endl;
    std::cout << "  getpkg update                         Update everything" << std::endl;
    std::cout << "  getpkg server add packages.example.com  Add a custom package server" << std::endl;
    std::cout << "  getpkg server remove packages.example.com  Remove a package server" << std::endl;
    std::cout << "  getpkg server list                    List all configured servers" << std::endl;
    std::cout << std::endl;
    std::cout << "ENVIRONMENT:" << std::endl;
    std::cout << "  SOS_WRITE_TOKEN                       Auth token for publishing tools" << std::endl;
    std::cout << std::endl;
    std::cout << "FILES:" << std::endl;
    std::cout << "  ~/.bashrc_getpkg                      Tool PATH and autocomplete setup" << std::endl;
    std::cout << "  ~/.config/getpkg/                     Tool metadata and configs" << std::endl;
    std::cout << "  ~/.local/bin/getpkg/                  Installed tool binaries" << std::endl;
}

int server_command(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: getpkg server <add|remove|list> [args...]" << std::endl;
        std::cerr << "  getpkg server add <url>       Add a new server" << std::endl;
        std::cerr << "  getpkg server remove <url>    Remove a server" << std::endl;
        std::cerr << "  getpkg server list            List all configured servers" << std::endl;
        return 1;
    }
    
    std::string subcommand = argv[2];
    ServerManager serverManager;
    
    // Load existing configuration
    if (!serverManager.loadConfiguration()) {
        std::cerr << "Failed to load server configuration" << std::endl;
        return 1;
    }
    
    if (subcommand == "add") {
        if (argc < 4) {
            std::cerr << "Usage: getpkg server add <url>" << std::endl;
            return 1;
        }
        
        std::string serverUrl = argv[3];
        
        // Validate server URL format
        if (serverUrl.empty()) {
            std::cerr << "Error: Server URL cannot be empty" << std::endl;
            return 1;
        }
        
        // Remove protocol if provided (we'll add it internally)
        if (serverUrl.find("http://") == 0) {
            serverUrl = serverUrl.substr(7);
        } else if (serverUrl.find("https://") == 0) {
            serverUrl = serverUrl.substr(8);
        }
        
        // Remove trailing slash if present
        if (!serverUrl.empty() && serverUrl.back() == '/') {
            serverUrl.pop_back();
        }
        
        std::cout << "Adding server: " << serverUrl << std::endl;
        
        if (serverManager.addServer(serverUrl)) {
            std::cout << "Successfully added server: " << serverUrl << std::endl;
            
            // Ask if user wants to add a write token
            std::cout << "Would you like to add a write token for this server? (y/N): ";
            std::string response;
            std::getline(std::cin, response);
            
            if (response == "y" || response == "Y" || response == "yes" || response == "Yes") {
                std::cout << "Enter write token for " << serverUrl << ": ";
                std::string token;
                std::getline(std::cin, token);
                
                if (!token.empty()) {
                    if (serverManager.setWriteToken(serverUrl, token)) {
                        std::cout << "Write token added successfully" << std::endl;
                    } else {
                        std::cerr << "Failed to save write token" << std::endl;
                    }
                }
            }
            
            return 0;
        } else {
            std::cerr << "Failed to add server: " << serverUrl << std::endl;
            return 1;
        }
        
    } else if (subcommand == "remove") {
        if (argc < 4) {
            std::cerr << "Usage: getpkg server remove <url>" << std::endl;
            return 1;
        }
        
        std::string serverUrl = argv[3];
        
        // Remove protocol if provided
        if (serverUrl.find("http://") == 0) {
            serverUrl = serverUrl.substr(7);
        } else if (serverUrl.find("https://") == 0) {
            serverUrl = serverUrl.substr(8);
        }
        
        // Remove trailing slash if present
        if (!serverUrl.empty() && serverUrl.back() == '/') {
            serverUrl.pop_back();
        }
        
        std::cout << "Removing server: " << serverUrl << std::endl;
        
        if (serverManager.removeServer(serverUrl)) {
            std::cout << "Successfully removed server: " << serverUrl << std::endl;
            return 0;
        } else {
            std::cerr << "Failed to remove server: " << serverUrl << std::endl;
            return 1;
        }
        
    } else if (subcommand == "list") {
        std::vector<std::string> servers = serverManager.getServers();
        
        if (servers.empty()) {
            std::cout << "No servers configured" << std::endl;
            return 0;
        }
        
        std::cout << std::endl;
        std::cout << "Configured servers:" << std::endl;
        std::cout << "+" << std::string(30, '-') << "+" << std::string(12, '-') << "+" << std::string(15, '-') << "+" << std::endl;
        std::cout << "|" << std::setw(30) << std::left << " Server URL" 
                  << "|" << std::setw(12) << std::left << " Default" 
                  << "|" << std::setw(15) << std::left << " Write Token"
                  << "|" << std::endl;
        std::cout << "+" << std::string(30, '-') << "+" << std::string(12, '-') << "+" << std::string(15, '-') << "+" << std::endl;
        
        std::string defaultServer = serverManager.getDefaultServer();
        
        for (const auto& server : servers) {
            bool isDefault = (server == defaultServer);
            bool hasToken = serverManager.hasWriteToken(server);
            
            std::string displayUrl = server;
            if (displayUrl.length() > 29) {
                displayUrl = displayUrl.substr(0, 26) + "...";
            }
            
            std::cout << "|" << std::setw(30) << std::left << (" " + displayUrl)
                      << "|" << std::setw(12) << std::left << (isDefault ? " Yes" : " No")
                      << "|" << std::setw(15) << std::left << (hasToken ? " Yes" : " No")
                      << "|" << std::endl;
        }
        
        std::cout << "+" << std::string(30, '-') << "+" << std::string(12, '-') << "+" << std::string(15, '-') << "+" << std::endl;
        std::cout << std::endl;
        std::cout << "Total servers: " << servers.size() << std::endl;
        
        // Show default publish server if different from default
        std::string defaultPublishServer = serverManager.getDefaultPublishServer();
        if (defaultPublishServer != defaultServer) {
            std::cout << "Default publish server: " << defaultPublishServer << std::endl;
        }
        
        return 0;
        
    } else {
        std::cerr << "Unknown server subcommand: " << subcommand << std::endl;
        std::cerr << "Use 'getpkg server' for usage information." << std::endl;
        return 1;
    }
}

int autocomplete_command(int argc, char* argv[]) {
    std::vector<std::string> args(argv + 2, argv + argc);
    
    // If no arguments, return all commands
    if (args.empty()) {
        std::cout << "install\n";
        std::cout << "uninstall\n";
        std::cout << "publish\n";
        std::cout << "unpublish\n";
        std::cout << "update\n";
        std::cout << "version\n";
        std::cout << "create\n";
        std::cout << "hash\n";
        std::cout << "list\n";
        std::cout << "clean\n";
        std::cout << "server\n";
        std::cout << "help\n";
        return 0;
    }
    
    const std::string& subcommand = args[0];
    
    // Handle autocompletion for specific commands
    if (subcommand == "install") {
        // For install, we could suggest popular packages or recently published ones
        // For now, just return empty (no specific completions)
        return 0;
    } else if (subcommand == "uninstall") {
        // For uninstall, list installed tools
        std::filesystem::path configDir = std::filesystem::path(std::getenv("HOME")) / ".config" / "getpkg";
        if (std::filesystem::exists(configDir)) {
            for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
                if (entry.path().extension() == ".json") {
                    std::cout << entry.path().stem().string() << "\n";
                }
            }
        }
        return 0;
    } else if (subcommand == "server") {
        // Handle server subcommand autocompletion
        if (args.size() == 1) {
            // Show server subcommands
            std::cout << "add\n";
            std::cout << "remove\n";
            std::cout << "list\n";
        } else if (args.size() == 2 && args[1] == "remove") {
            // For server remove, list configured servers
            ServerManager serverManager;
            if (serverManager.loadConfiguration()) {
                std::vector<std::string> servers = serverManager.getServers();
                for (const auto& server : servers) {
                    std::cout << server << "\n";
                }
            }
        }
        return 0;
    } else if (subcommand == "unpublish") {
        // For unpublish, we could suggest installed tools
        std::filesystem::path configDir = std::filesystem::path(std::getenv("HOME")) / ".config" / "getpkg";
        if (std::filesystem::exists(configDir)) {
            for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
                if (entry.path().extension() == ".json") {
                    std::string toolName = entry.path().stem().string();
                    std::cout << toolName << "\n";
                }
            }
        }
        return 0;
    } else if (subcommand == "publish") {
        // For publish, suggest architecture suffixes after tool name
        if (args.size() >= 2) {
            // If we have tool_name already, suggest architectures
            std::cout << "x86_64\n";
            std::cout << "aarch64\n";
            std::cout << "universal\n";
        }
        return 0;
    } else if (subcommand == "unpublish") {
        // For unpublish, list installed tools (similar to uninstall)
        std::filesystem::path configDir = std::filesystem::path(std::getenv("HOME")) / ".config" / "getpkg";
        if (std::filesystem::exists(configDir)) {
            for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
                if (entry.path().extension() == ".json") {
                    std::string toolName = entry.path().stem().string();
                    std::cout << toolName << "\n";
                    // Also suggest with architecture suffixes
                    std::cout << toolName << ":x86_64\n";
                    std::cout << toolName << ":aarch64\n";
                    std::cout << toolName << ":universal\n";
                }
            }
        }
        return 0;
    } else if (subcommand == "create") {
        // For create, no specific completions (tool name and directory are user-defined)
        return 0;
    } else if (subcommand == "hash") {
        // For hash, suggest file extensions
        if (args.size() >= 2) {
            std::cout << "*.tgz\n";
            std::cout << "*.tar.gz\n";
        }
        return 0;
    }
    
    // No specific completions for other commands
    return 0;
}

// Migration check and execution
bool checkAndPerformMigration() {
    try {
        MigrationManager migrationManager;
        
        if (migrationManager.needsMigration()) {
            std::cout << "Migrating getpkg configuration to multi-server format..." << std::endl;
            
            if (migrationManager.performMigration()) {
                auto result = migrationManager.getLastMigrationResult();
                std::cout << "Migration completed successfully!" << std::endl;
                
                if (result.migratedPackages > 0) {
                    std::cout << "  - Migrated " << result.migratedPackages << " package(s)" << std::endl;
                }
                if (result.serverConfigMigrated) {
                    std::cout << "  - Updated server configuration" << std::endl;
                }
                if (result.packageDirectoryCreated) {
                    std::cout << "  - Created packages directory structure" << std::endl;
                }
                
                if (!result.warnings.empty()) {
                    std::cout << "Migration warnings:" << std::endl;
                    for (const auto& warning : result.warnings) {
                        std::cout << "  - " << warning << std::endl;
                    }
                }
                
                return true;
            } else {
                auto result = migrationManager.getLastMigrationResult();
                std::cerr << "Migration failed!" << std::endl;
                
                if (!result.errors.empty()) {
                    std::cerr << "Migration errors:" << std::endl;
                    for (const auto& error : result.errors) {
                        std::cerr << "  - " << error << std::endl;
                    }
                }
                
                if (migrationManager.canRollback()) {
                    std::cerr << "Attempting rollback..." << std::endl;
                    if (migrationManager.performRollback()) {
                        std::cerr << "Rollback successful. Configuration restored to previous state." << std::endl;
                    } else {
                        std::cerr << "Rollback failed. Manual intervention may be required." << std::endl;
                    }
                }
                
                return false;
            }
        }
        
        return true; // No migration needed
        
    } catch (const std::exception& e) {
        std::cerr << "Migration error: " << e.what() << std::endl;
        return false;
    }
}

} // end anonymous namespace

int main(int argc, char* argv[]) {
    // Perform migration check before any other operations
    if (!checkAndPerformMigration()) {
        std::cerr << "Failed to migrate configuration. Some functionality may not work correctly." << std::endl;
        // Continue execution but warn user
    }
    if (argc < 2) {
        show_help();
        return 0;
    }
    std::string command = argv[1];
    if (command == "install") {
        return install_tool(argc, argv);
    } else if (command == "uninstall") {
        return uninstall_tool(argc, argv);
    } else if (command == "publish") {
        return publish_tool(argc, argv);
    } else if (command == "unpublish") {
        return unpublish_tool(argc, argv);
    } else if (command == "update") {
        return update_tool(argc, argv);
    } else if (command == "autocomplete") {
        return autocomplete_command(argc, argv);
    } else if (command == "version") {
        std::cout << dropshell::VERSION << std::endl;
    } else if (command == "create") {
        return create_tool(argc, argv);
    } else if (command == "hash") {
        return hash_command(argc, argv);
    } else if (command == "list") {
        return list_packages(argc, argv);
    } else if (command == "clean") {
        return clean_tool(argc, argv);
    } else if (command == "server") {
        return server_command(argc, argv);
    } else if (command == "help") {
        show_help();
    } else {
        std::cerr << "Unknown command: " << command << std::endl;
        std::cerr << "Use 'getpkg help' for usage information." << std::endl;
        return 1;
    }
    return 0;
}