#include "lumacs/command_system.hpp"
#include "lumacs/editor_core.hpp" // Now included here for full definition
#include "lumacs/minibuffer_manager.hpp" // Added for interactive argument gathering
#include "lumacs/i_command_target.hpp" // Added for ICommandTarget
#include "lumacs/logger.hpp"
#include <spdlog/spdlog.h>
#include <algorithm> // For std::sort
#include <vector>    // For std::vector
#include <string_view> // For std::string_view

namespace lumacs {

// --- CommandContext Implementations ---
std::optional<std::string> CommandContext::get_string_arg(size_t index) const {
    if (index < args_.size()) {
        return args_[index];
    }
    return std::nullopt;
}

std::optional<int> CommandContext::get_int_arg(size_t index) const {
    if (index < args_.size()) {
        try {
            return std::stoi(args_[index]);
        } catch (const std::invalid_argument& e) {
            spdlog::error("CommandContext::get_int_arg: Invalid argument for stoi: {}", args_[index]);
        } catch (const std::out_of_range& e) {
            spdlog::error("CommandContext::get_int_arg: Out of range for stoi: {}", args_[index]);
        }
    }
    return std::nullopt;
}

// These will be properly implemented once the CommandSystem can handle asynchronous interactive input.
// For now, they return nullopt, relying on the CommandSystem to manage the interactive process.
std::optional<std::string> CommandContext::read_string(const std::string& prompt) { 
    (void)prompt; 
    return std::nullopt; 
}
std::optional<std::string> CommandContext::read_file_path(const std::string& prompt) { 
    (void)prompt; 
    return std::nullopt; 
}


// --- CommandSystem Implementations ---
CommandSystem::CommandSystem(EditorCore& core, MinibufferManager& minibuffer_manager)
    : core_(core), minibuffer_manager_(minibuffer_manager) {
    // Register core commands here, or ensure they are registered via Lua
}

void CommandSystem::register_command(const std::string& name, CommandFunction function,
                                     const std::string& doc_string, bool interactive, 
                                     std::string interactive_spec, std::vector<std::string> aliases) {
    // If the canonical command already exists, overwrite it.
    commands_.insert_or_assign(name, Command(name, std::move(function), doc_string, interactive, std::move(interactive_spec), aliases));

    // Register aliases
    for (const auto& alias : aliases) {
        // If an alias name clashes with an existing canonical command, prioritize the canonical command.
        // Otherwise, map the alias to the canonical command name.
        if (commands_.find(alias) == commands_.end()) {
            alias_map_[alias] = name;
        } else {
            spdlog::warn("Alias '{}' for command '{}' conflicts with existing canonical command. Alias ignored.", alias, name);
        }
    }
}

// Helper to split interactive_spec string into individual specifiers
static std::vector<std::string_view> parse_interactive_spec(std::string_view spec) {
    std::vector<std::string_view> parts;
    size_t start = 0;
    size_t end = spec.find_first_of(';');
    while (end != std::string_view::npos) {
        parts.push_back(spec.substr(start, end - start));
        start = end + 1;
        end = spec.find_first_of(';', start);
    }
    parts.push_back(spec.substr(start)); // Add the last part
    return parts;
}

CommandResult CommandSystem::process_next_interactive_argument() {
    if (!current_interactive_command_ || current_interactive_command_->interactive_spec.empty()) {
        // This should not happen if called correctly
        current_interactive_command_.reset();
        return {CommandStatus::Failure, "Internal error: No interactive command or spec."};
    }

    auto specs = parse_interactive_spec(current_interactive_command_->interactive_spec);

    if (current_interactive_spec_index_ >= specs.size()) {
        // All arguments gathered, execute the command
        CommandContext context(core_, minibuffer_manager_, gathered_interactive_args_); // Pass core_ as ICommandTarget&
        CommandResult result = current_interactive_command_->function(context);
        current_interactive_command_.reset(); // Clear state after execution
        gathered_interactive_args_.clear();
        current_interactive_spec_index_ = 0;
        return result;
    }

    std::string_view current_spec = specs[current_interactive_spec_index_];
    std::string prompt;
    MinibufferMode mode = MinibufferMode::Command; // Default mode

    // Determine prompt and mode based on interactive_spec
    if (current_spec == "s") {
        prompt = "String: ";
        mode = MinibufferMode::ystring;
    } else if (current_spec == "f") {
        prompt = "Find file: ";
        mode = MinibufferMode::FilePath;
    } else if (current_spec == "b") {
        prompt = "Switch to buffer: ";
        mode = MinibufferMode::BufferName;
    } else if (current_spec == "n") {
        prompt = "Number: ";
        mode = MinibufferMode::ystring; // Numbers are read as strings, then parsed
    }
    // TODO: Add more specifiers as needed (e.g., region, yes/no)

    // Capture `this` for the callbacks
    auto self = this;
    minibuffer_manager_.activate_minibuffer(
        mode,
        prompt,
        [self](const std::string& input) {
            self->gathered_interactive_args_.push_back(input);
            self->current_interactive_spec_index_++;
            // Try to process the next argument or execute the command
            // Try to process the next argument or execute the command
            CommandResult next_step_result = self->process_next_interactive_argument(); // Recursively call to continue
            // If the command fully executed and returned a message, set it.
            if (next_step_result.status != CommandStatus::PendingInput && !next_step_result.message.empty()) {
                self->core_.set_message(next_step_result.message);
            }
        },
        [self]() {
            // On cancel, clear the interactive state
            self->current_interactive_command_.reset();
            self->gathered_interactive_args_.clear();
            self->current_interactive_spec_index_ = 0;
            // TODO: How to propagate cancellation result?
        }
    );

    return {CommandStatus::PendingInput, "Waiting for user input."};
}


void CommandSystem::clear_commands() {
    commands_.clear();
    alias_map_.clear();
}

CommandResult CommandSystem::execute(const std::string& name, const std::vector<std::string>& args) {
    std::string canonical_name = name;
    auto alias_it = alias_map_.find(name);
    if (alias_it != alias_map_.end()) {
        canonical_name = alias_it->second;
    }

    auto it = commands_.find(canonical_name);
    if (it != commands_.end()) {
        try {
            // For non-interactive execution, create a context with provided args
            CommandContext context(core_, minibuffer_manager_, args); // Pass core_ as ICommandTarget&
            return it->second.function(context);
        } catch (const std::exception& e) {
            return {CommandStatus::Failure, "Command '" + canonical_name + "' failed: " + e.what()};
        } catch (...) {
            return {CommandStatus::Failure, "Command '" + canonical_name + "' failed with unknown error."};
        }
    }
    return {CommandStatus::Failure, "Unknown command or alias: " + name};
}

CommandResult CommandSystem::execute_interactive(const std::string& name) {
    // Resolve alias to canonical name first
    std::string canonical_name = name;
    auto alias_it = alias_map_.find(name);
    if (alias_it != alias_map_.end()) {
        canonical_name = alias_it->second;
    }

    // If an interactive command is already pending, we shouldn't start a new one.
    // This is a simplification; a more robust system might queue or disallow.
    if (current_interactive_command_) {
        return {CommandStatus::Failure, "Another interactive command is already pending input."};
    }

    auto it = commands_.find(canonical_name);
    if (it == commands_.end()) {
        return {CommandStatus::Failure, "Unknown command or alias: " + name};
    }
    if (!it->second.interactive) {
        return {CommandStatus::Failure, "Command '" + canonical_name + "' is not interactive."};
    }
    if (it->second.interactive_spec.empty()) {
        // If interactive but no spec, just execute directly (no args, or uses default)
        CommandContext context(core_, minibuffer_manager_, {}); // Pass core_ as ICommandTarget&
        return it->second.function(context);
    }

    // Start a new interactive command sequence
    current_interactive_command_ = it->second; // Copy the Command struct
    current_interactive_spec_index_ = 0;
    gathered_interactive_args_.clear();

    return process_next_interactive_argument();
}

std::vector<std::string> CommandSystem::get_command_names() const {
    std::vector<std::string> names;
    names.reserve(commands_.size() + alias_map_.size());
    for (const auto& pair : commands_) {
        names.push_back(pair.first);
    }
    for (const auto& pair : alias_map_) {
        names.push_back(pair.first);
    }
    std::sort(names.begin(), names.end());
    return names;
}

std::optional<std::string> CommandSystem::get_command_doc_string(const std::string& name) const {
    std::string canonical_name = name;
    auto alias_it = alias_map_.find(name);
    if (alias_it != alias_map_.end()) {
        canonical_name = alias_it->second;
    }

    auto it = commands_.find(canonical_name);
    if (it != commands_.end()) {
        return it->second.doc_string;
    }
    return std::nullopt;
}

std::optional<std::string> CommandSystem::get_command_interactive_spec(const std::string& name) const {
    std::string canonical_name = name;
    auto alias_it = alias_map_.find(name);
    if (alias_it != alias_map_.end()) {
        canonical_name = alias_it->second;
    }

    auto it = commands_.find(canonical_name);
    if (it != commands_.end()) {
        if (it->second.interactive) {
            return it->second.interactive_spec;
        }
    }
    return std::nullopt;
}

} // namespace lumacs