#pragma once

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <chrono>
#include <optional>
#include "lumacs/command_system.hpp" // Include the full definition of CommandResult

// Forward declaration for CommandSystem
namespace lumacs {
    class CommandSystem;
}

namespace lumacs {

/// @brief Represents the base key (without modifiers).
enum class BaseKey : uint16_t {
    Unknown = 0,
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
    D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, // Digits
    F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, // Function keys
    Space, Return, Tab, Escape, Backspace, Delete,
    ArrowUp, ArrowDown, ArrowLeft, ArrowRight,
    Home, End, PageUp, PageDown, Insert,
    Semicolon, Equal, Comma, Minus, Period, Slash, Backtick,
    LeftBracket, Backslash, RightBracket, Quote,
    NumLock, ScrollLock, PrintScreen, Pause,
    // Add more as needed
};

/// @brief Represents key modifiers as a bitmask.
enum class Modifier : uint16_t {
    None = 0,
    Control = 1 << 0,
    Meta = 1 << 1, // Alt key, Option key
    Shift = 1 << 2,
};

/// @brief Represents a single key in a key sequence.
struct Key {
    BaseKey base_key = BaseKey::Unknown;
    Modifier modifiers = Modifier::None;
    
    Key() = default;
    explicit Key(BaseKey bk, Modifier mods = Modifier::None) : base_key(bk), modifiers(mods) {}
    explicit Key(const std::string& key_name); // To be implemented in .cpp
    
    /// @brief Parse a key string like "C-x" or "M-S-a" into a Key structure.
    static Key parse(const std::string& key_str); // To be implemented in .cpp
    /// @brief Convert Key back to canonical string representation.
    std::string to_string() const; // To be implemented in .cpp
    
    bool operator==(const Key& other) const;
    bool operator<(const Key& other) const;
};

/// @brief Represents a sequence of keys (e.g., "C-x C-f").
class KeySequence {
public:
    KeySequence() = default;
    explicit KeySequence(const std::vector<Key>& keys);
    explicit KeySequence(const std::string& key_sequence_str);
    
    /// @brief Add a key to the sequence.
    void add_key(const Key& key);
    void add_key(const std::string& key_str);
    
    /// @brief Get the keys in this sequence.
    const std::vector<Key>& keys() const { return keys_; }
    
    /// @brief Check if this sequence is empty.
    bool empty() const { return keys_.empty(); }
    
    /// @brief Get the length of the sequence.
    size_t length() const { return keys_.size(); }
    
    /// @brief Check if this sequence is a prefix of another sequence.
    bool is_prefix_of(const KeySequence& other) const;
    
    /// @brief Check if this sequence has the given sequence as a prefix.
    bool has_prefix(const KeySequence& prefix) const;
    
    /// @brief Get string representation like "C-x 2".
    std::string to_string() const;
    
    /// @brief Clear the sequence.
    void clear();
    
    /// @brief Get a subsequence starting from index.
    KeySequence subsequence(size_t start_index) const;
    
    bool operator==(const KeySequence& other) const;
    bool operator<(const KeySequence& other) const;

private:
    std::vector<Key> keys_;
};



/// @brief Represents a key binding with its associated function.
struct KeyBinding {
    KeySequence sequence;       ///< The key sequence that triggers this binding.
    std::string command_name;   ///< The name of the command to execute.
    std::string description;    ///< Human-readable description.
    
    KeyBinding() = default;
    KeyBinding(const KeySequence& seq, std::string cmd_name, std::string desc = "");
    KeyBinding(const std::string& key_str, std::string cmd_name, const std::string& description = "");
};

/// @brief Represents the result of processing a key in the binding manager.
enum class KeyResult {
    Unbound,        ///< No binding found, key not handled.
    Executed,       ///< Binding found and executed successfully.
    Failed,         ///< Binding found but execution failed.
    Partial,        ///< Key is part of a multi-key sequence, waiting for more keys.
    Timeout         ///< Partial sequence timed out.
};

/// @brief Detailed result of a key processing operation.
struct KeyProcessingResult {
    KeyResult type;
    std::optional<CommandResult> command_result; // Relevant if type is Executed or Failed
};

/// @brief Central manager for all key bindings.
/// 
/// Handles:
/// - Registration of global key bindings.
/// - Processing of key input (single and multi-key sequences).
/// - Prefix key logic (e.g., C-x waiting for next key).
class KeyBindingManager {
public:
    KeyBindingManager(CommandSystem* command_system);
    ~KeyBindingManager() = default;
    
    /// @brief Bind a key sequence to a function.
    void bind(const KeySequence& sequence, std::string cmd_name, const std::string& description = "");
    void bind(const std::string& key_str, std::string cmd_name, const std::string& description = "");
    
    /// @brief Unbind a key sequence.
    void unbind(const KeySequence& sequence);
    void unbind(const std::string& key_str);
    
    /// @brief Process a single key press.
    /// @return Result indicating if key was bound, partial, etc.
    KeyProcessingResult process_key(const Key& key);
    KeyProcessingResult process_key(const std::string& key_str);
    
    /// @brief Check if a sequence has any bindings (exact or partial).
    bool has_binding(const KeySequence& sequence) const;
    
    /// @brief Check if a sequence has an exact binding.
    bool has_exact_binding(const KeySequence& sequence) const;
    
    /// @brief Check if a sequence is a prefix for other bindings.
    bool has_prefix_bindings(const KeySequence& sequence) const;
    
    /// @brief Clear current partial sequence (e.g., on timeout or escape).
    void clear_partial_sequence();
    
    /// @brief Get current partial sequence for display purposes.
    const KeySequence& current_partial_sequence() const { return current_sequence_; }
    
    /// @brief Check if we're currently building a multi-key sequence.
    bool is_building_sequence() const;
    
    /// @brief Get string representation of current sequence for display.
    std::string current_sequence_display() const;
    
    /// @brief Get all registered bindings (for debugging/help).
    std::vector<KeyBinding> get_all_bindings() const;
    
    /// @brief Set timeout for multi-key sequences.
    void set_sequence_timeout(std::chrono::milliseconds timeout);
    
    /// @brief Check if current partial sequence has timed out.
    bool has_sequence_timed_out() const;

private:
    // Trie node structure
    struct TrieNode {
        std::optional<KeyBinding> binding; // Stores binding if this node represents the end of a sequence
        std::map<Key, std::unique_ptr<TrieNode>> children;
        int ref_count = 0; // Number of bindings passing through this node

        TrieNode() = default;

        // Prevent copying, allow moving
        TrieNode(const TrieNode&) = delete;
        TrieNode& operator=(const TrieNode&) = delete;
        TrieNode(TrieNode&&) noexcept = default;
        TrieNode& operator=(TrieNode&&) noexcept = default;
    };
    
    std::unique_ptr<TrieNode> root_; // Root of the trie
    KeySequence current_sequence_;
    std::chrono::steady_clock::time_point sequence_start_time_;
    std::chrono::milliseconds sequence_timeout_;
    CommandSystem* command_system_ = nullptr; // Dependency on CommandSystem
    
    // Helper function to find a node in the trie
    TrieNode* find_node(const KeySequence& sequence) const;
    
    // Helper to traverse trie and collect all bindings
    void collect_bindings(TrieNode* node, KeySequence current_prefix, std::vector<KeyBinding>& result) const;
};

} // namespace lumacs