#include "DropshellScriptManager.hpp"
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>
#include <algorithm>
#include <iostream>

namespace dropshelltool {
    extern const std::filesystem::path DROPSHELL_RC_PATH;
}

DropshellScriptManager::DropshellScriptManager() {}

// Helper function to trim whitespace from both ends of a string
static std::string trim(const std::string& s) {
    auto start = s.begin();
    while (start != s.end() && std::isspace(*start)) ++start;
    auto end = s.end();
    do { --end; } while (std::distance(start, end) > 0 && std::isspace(*end));
    return std::string(start, end + 1);
}

void DropshellScriptManager::ensureExists() const {
    if (!std::filesystem::exists(dropshelltool::DROPSHELL_RC_PATH)) {
        std::filesystem::create_directories(dropshelltool::DROPSHELL_RC_PATH.parent_path());
        std::ofstream outfile(dropshelltool::DROPSHELL_RC_PATH, std::ios::trunc);
        outfile << "# This file is managed by getpkg. Do not edit manually!" << std::endl;
        outfile.close();
    }
}

void DropshellScriptManager::addToolEntry(const std::string& toolName, const std::string& toolDir) {
    // Create symlinks instead of adding to PATH
    createSymlinks(toolName, toolDir);
}

void DropshellScriptManager::removeToolEntry(const std::string& toolName) {
    // Remove symlinks instead of PATH entries
    removeSymlinks(toolName);
}

void DropshellScriptManager::addAutocomplete(const std::string& toolName) {
    ensureExists();
    std::ifstream infile(dropshelltool::DROPSHELL_RC_PATH);
    std::vector<std::string> lines;
    std::string line;

    std::string funcName = "_" + toolName + "_autocomplete";
    
    std::string blockStart = "# GETPKG-AUTOCOMPLETE-START: " + toolName;
    std::string blockEnd = "# GETPKG-AUTOCOMPLETE-END: " + toolName;
    
    std::vector<std::string> funcLines;
    funcLines.push_back(blockStart);
    funcLines.push_back(funcName + "() {");
    funcLines.push_back("    local cur=\"${COMP_WORDS[COMP_CWORD]}\"");
    funcLines.push_back("    mapfile -t completions < <(" + toolName + " autocomplete \"${COMP_WORDS[@]:1:${COMP_CWORD}-1}\")");
    funcLines.push_back("    mapfile -t COMPREPLY < <(compgen -W \"${completions[*]}\" -- \"$cur\")");
    funcLines.push_back("}");
    funcLines.push_back(blockEnd);

    std::string completeLine = "complete -F " + funcName + " " + toolName + " # autocomplete: " + toolName;
    bool blockFound = false;
    bool inBlock = false;
    while (std::getline(infile, line)) {
        if (!inBlock && trim(line) == blockStart) {
            blockFound = true;
            inBlock = true;
            continue;
        }
        if (inBlock) {
            if (trim(line) == blockEnd) {
                inBlock = false;
                // Add all function lines
                for (const auto& funcLine : funcLines) {
                    lines.push_back(funcLine);
                }
            }
            continue; // Skip all lines in the old block
        }
        else
            lines.push_back(line);
    }
    infile.close();
    if (!blockFound) {
        // Add all function lines
        for (const auto& funcLine : funcLines) {
            lines.push_back(funcLine);
        }
    }
    std::ofstream outfile(dropshelltool::DROPSHELL_RC_PATH, std::ios::trunc);
    for (const auto& l : lines) outfile << l << "\n";
    outfile.close();
}

void DropshellScriptManager::removeAutocomplete(const std::string& toolName) {
    ensureExists();
    std::ifstream infile(dropshelltool::DROPSHELL_RC_PATH);
    std::vector<std::string> lines;
    std::string line;
    
    std::string blockStart = "# GETPKG-AUTOCOMPLETE-START: " + toolName;
    std::string blockEnd = "# GETPKG-AUTOCOMPLETE-END: " + toolName;
    bool inBlock = false;
    
    while (std::getline(infile, line)) {
        if (trim(line) == blockStart) {
            inBlock = true;
            continue;
        }
        if (trim(line) == blockEnd) {
            inBlock = false;
            continue;
        }
        if (inBlock) {
            continue;
        }
        // Also skip the complete line
        if (line.find("# autocomplete: " + toolName) != std::string::npos) {
            continue;
        }
        lines.push_back(line);
    }
    infile.close();
    
    std::ofstream outfile(dropshelltool::DROPSHELL_RC_PATH, std::ios::trunc);
    for (const auto& l : lines) outfile << l << "\n";
    outfile.close();
}

void DropshellScriptManager::createSymlinks(const std::string& toolName, const std::string& toolDir) {
    std::string home = std::getenv("HOME");
    std::filesystem::path getpkgBinDir = std::filesystem::path(home) / ".local/bin/getpkg";
    
    // Ensure the getpkg bin directory exists
    std::filesystem::create_directories(getpkgBinDir);
    
    // Remove existing symlinks for this tool first
    removeSymlinks(toolName);
    
    // Find all executable files in the tool directory
    if (std::filesystem::exists(toolDir)) {
        for (const auto& entry : std::filesystem::directory_iterator(toolDir)) {
            if (entry.is_regular_file()) {
                auto perms = std::filesystem::status(entry).permissions();
                // Check if the file is executable (any execute bit set)
                if ((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none ||
                    (perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none ||
                    (perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) {
                    
                    std::string fileName = entry.path().filename().string();
                    std::filesystem::path symlinkPath = getpkgBinDir / fileName;
                    
                    // Skip if a directory already exists with that name (tool installation directory)
                    if (std::filesystem::exists(symlinkPath) && std::filesystem::is_directory(symlinkPath)) {
                        continue;
                    }
                    
                    // Remove existing symlink if it exists
                    if (std::filesystem::exists(symlinkPath) && std::filesystem::is_symlink(symlinkPath)) {
                        std::filesystem::remove(symlinkPath);
                    }
                    
                    // Create symlink
                    try {
                        std::filesystem::create_symlink(entry.path(), symlinkPath);
                        std::cout << "Created symlink: " << symlinkPath << " -> " << entry.path() << std::endl;
                    } catch (const std::exception& e) {
                        std::cerr << "Warning: Failed to create symlink " << symlinkPath << ": " << e.what() << std::endl;
                    }
                }
            }
        }
    }
}

void DropshellScriptManager::removeSymlinks(const std::string& toolName) {
    std::string home = std::getenv("HOME");
    std::filesystem::path getpkgBinDir = std::filesystem::path(home) / ".local/bin/getpkg";
    std::filesystem::path toolDir = std::filesystem::path(home) / ".getpkg" / toolName;
    
    if (!std::filesystem::exists(getpkgBinDir)) {
        return;
    }
    
    // Remove symlinks that point to files in the tool directory
    for (const auto& entry : std::filesystem::directory_iterator(getpkgBinDir)) {
        if (entry.is_symlink()) {
            try {
                std::filesystem::path target = std::filesystem::read_symlink(entry.path());
                // Check if the symlink points to something in the tool directory
                if (target.string().find(toolDir.string()) == 0) {
                    std::filesystem::remove(entry.path());
                    std::cout << "Removed symlink: " << entry.path() << std::endl;
                }
            } catch (const std::exception& e) {
                std::cerr << "Warning: Failed to process symlink " << entry.path() << ": " << e.what() << std::endl;
            }
        }
    }
}