#include "lumacs/completion_system.hpp"
#include "lumacs/editor_core.hpp" // For accessing EditorCore for completion data
#include <algorithm> // For std::remove_if
#include <filesystem> // For file path completion

namespace lumacs {

// --- Concrete CompletionSource Implementations ---

/// @brief Completion source for editor commands.
class CommandCompletionSource : public ICompletionSource {
public:
    CommandCompletionSource(EditorCore& core) : core_(core) {}

    std::vector<CompletionCandidate> get_candidates(const std::string& input_text) override {
        std::vector<CompletionCandidate> candidates;
        std::vector<std::string> all_command_names = core_.command_system().get_command_names(); // Get actual command names
        
        for (const auto& cmd_name : all_command_names) {
            if (cmd_name.rfind(input_text, 0) == 0) { // Check if command starts with input_text
                std::string description = "";
                if (auto doc_opt = core_.command_system().get_command_doc_string(cmd_name)) {
                    description = *doc_opt;
                }
                candidates.emplace_back(cmd_name, 0, "", description); // Use actual description
            }
        }
        return candidates;
    }

private:
    EditorCore& core_;
};

/// @brief Completion source for buffer names.
class BufferNameCompletionSource : public ICompletionSource {
public:
    BufferNameCompletionSource(EditorCore& core) : core_(core) {}

    std::vector<CompletionCandidate> get_candidates(const std::string& input_text) override {
        std::vector<CompletionCandidate> candidates;
        std::vector<std::string> all_buffer_names = core_.get_buffer_names(); // Get actual buffer names
        for (const auto& name : all_buffer_names) {
            if (name.rfind(input_text, 0) == 0) {
                candidates.emplace_back(name);
            }
        }
        return candidates;
    }

private:
    EditorCore& core_;
};

// Include cstdlib for std::getenv
#include <cstdlib> 

/// @brief Completion source for file paths.
class FilePathCompletionSource : public ICompletionSource {
public:
    FilePathCompletionSource(EditorCore& core) : core_(core) {}

    std::vector<CompletionCandidate> get_candidates(const std::string& input_text) override {
        std::vector<CompletionCandidate> candidates;
        std::string expanded_input = expand_tilde(input_text);
        
        std::filesystem::path path_obj(expanded_input);
        std::filesystem::path dir;
        std::string prefix;

        // Determine directory to search and prefix to match
        if (input_text.empty()) {
            dir = std::filesystem::current_path();
            prefix = "";
        } else if (input_text.back() == '/') {
            dir = expanded_input;
            prefix = "";
        } else {
            // If expanded_input is just a directory (like "/tmp"), parent_path is "/" and filename is "tmp".
            // But we want to search INSIDE /tmp if the user typed "/tmp/" (handled above).
            // If user typed "/tmp", we search in "/" for "tmp*".
            // The filesystem::path parsing handles this:
            // path("/tmp").parent_path() -> "/"
            // path("/tmp").filename() -> "tmp"
            
            dir = path_obj.parent_path();
            prefix = path_obj.filename().string();
            
            // Handle relative paths (empty parent means current dir, unless it's root)
            if (dir.empty()) {
                if (path_obj.is_absolute()) {
                    // Should typically not happen for unix paths unless it is root?
                    // path("/").parent_path() is "/".
                } else {
                    dir = std::filesystem::current_path();
                }
            }
        }

        try {
            // Check if directory exists
            if (std::filesystem::exists(dir) && std::filesystem::is_directory(dir)) {
                for (const auto& entry : std::filesystem::directory_iterator(dir)) {
                    std::string filename = entry.path().filename().string();
                    
                    // Skip hidden files unless prefix starts with '.'
                    if (filename.size() > 0 && filename[0] == '.' && (prefix.empty() || prefix[0] != '.')) {
                        continue;
                    }

                    // Check prefix match
                    if (filename.rfind(prefix, 0) == 0) {
                        // Reconstruct the completion text based on ORIGINAL input
                        std::string result_str;
                        
                        if (input_text.empty()) {
                            result_str = filename;
                        } else if (input_text.back() == '/') {
                            result_str = input_text + filename;
                        } else {
                            // Replace the partial filename in input with the full match
                            size_t last_sep = input_text.find_last_of('/');
                            if (last_sep != std::string::npos) {
                                result_str = input_text.substr(0, last_sep + 1) + filename;
                            } else {
                                result_str = filename;
                            }
                        }

                        if (entry.is_directory()) {
                            result_str += "/";
                        }
                        
                        candidates.emplace_back(result_str);
                    }
                }
            }
        } catch (...) {
            // Ignore filesystem errors (permissions, etc.)
        }
        
        return candidates;
    }

private:
    [[maybe_unused]] EditorCore& core_; 

    std::string expand_tilde(const std::string& path) {
        if (path.empty()) return path;
        if (path[0] == '~') {
            const char* home = std::getenv("HOME");
            if (home) {
                // Handle "~" -> "/home/user" and "~/foo" -> "/home/user/foo"
                if (path.length() == 1) return std::string(home);
                if (path[1] == '/') return std::string(home) + path.substr(1);
            }
        }
        return path;
    }
};

/// @brief Completion source for theme names.
class ThemeNameCompletionSource : public ICompletionSource {
public:
    ThemeNameCompletionSource(EditorCore& core) : core_(core) {}

    std::vector<CompletionCandidate> get_candidates(const std::string& input_text) override {
        std::vector<CompletionCandidate> candidates;
        std::vector<std::string> all_theme_names = core_.theme_manager().theme_names();
        for (const auto& name : all_theme_names) {
            if (name.rfind(input_text, 0) == 0) {
                candidates.emplace_back(name);
            }
        }
        return candidates;
    }

private:
    [[maybe_unused]] EditorCore& core_; // Reserved for future filesystem operations
};


// --- CompletionSystem Implementation ---

CompletionSystem::CompletionSystem(EditorCore& core) : core_(core) {
    // Register default completion sources
    register_source(MinibufferMode::Command, std::make_unique<CommandCompletionSource>(core));
    register_source(MinibufferMode::BufferName, std::make_unique<BufferNameCompletionSource>(core));
    register_source(MinibufferMode::FilePath, std::make_unique<FilePathCompletionSource>(core));
    register_source(MinibufferMode::ThemeName, std::make_unique<ThemeNameCompletionSource>(core));
}

void CompletionSystem::register_source(MinibufferMode mode, std::unique_ptr<ICompletionSource> source) {
    sources_[mode] = std::move(source);
}

std::vector<CompletionCandidate> CompletionSystem::get_candidates_for_mode(MinibufferMode mode, const std::string& input_text) {
    auto it = sources_.find(mode);
    if (it != sources_.end()) {
        return it->second->get_candidates(input_text);
    }
    return {}; // No completion source registered for this mode
}

} // namespace lumacs
