#include "lumacs/theme.hpp"
#include <ncurses.h>
#include <algorithm>
#include <climits>

namespace lumacs {

static std::map<Color, int> color_cache;
static int next_color_id = 8; // Start after the basic 8 colors

int Color::to_ncurses_color() const {
    // Handle default colors
    if (r == -1 && g == -1 && b == -1) {
        return -1; // Use default
    }
    
    // Check cache first
    auto it = color_cache.find(*this);
    if (it != color_cache.end()) {
        return it->second;
    }
    
    // Check if we can create new colors
    if (can_change_color() && next_color_id < COLOR_PAIRS) {
        int color_id = next_color_id++;
        // Scale RGB values to 0-1000 range for ncurses
        int nr = (r * 1000) / 255;
        int ng = (g * 1000) / 255;
        int nb = (b * 1000) / 255;
        
        init_color(color_id, nr, ng, nb);
        color_cache[*this] = color_id;
        return color_id;
    }
    
    // Fallback to closest basic color
    int min_dist = INT_MAX;
    int closest = COLOR_WHITE;
    
    struct BasicColor { int id; int r, g, b; };
    BasicColor basic_colors[] = {
        {COLOR_BLACK, 0, 0, 0},
        {COLOR_RED, 255, 0, 0},
        {COLOR_GREEN, 0, 255, 0},
        {COLOR_YELLOW, 255, 255, 0},
        {COLOR_BLUE, 0, 0, 255},
        {COLOR_MAGENTA, 255, 0, 255},
        {COLOR_CYAN, 0, 255, 255},
        {COLOR_WHITE, 255, 255, 255}
    };
    
    for (auto& basic : basic_colors) {
        int dr = r - basic.r;
        int dg = g - basic.g;
        int db = b - basic.b;
        int dist = dr*dr + dg*dg + db*db;
        if (dist < min_dist) {
            min_dist = dist;
            closest = basic.id;
        }
    }
    
    return closest;
}

std::string Theme::element_to_face_name(ThemeElement element) {
    switch (element) {
        case ThemeElement::Normal: return "default";
        case ThemeElement::Keyword: return "font-lock-keyword-face";
        case ThemeElement::String: return "font-lock-string-face";
        case ThemeElement::Comment: return "font-lock-comment-face";
        case ThemeElement::Function: return "font-lock-function-name-face";
        case ThemeElement::Type: return "font-lock-type-face";
        case ThemeElement::Number: return "font-lock-constant-face"; // Mapping number to constant for now
        case ThemeElement::Constant: return "font-lock-constant-face";
        case ThemeElement::Error: return "error";
        case ThemeElement::StatusLine: return "mode-line";
        case ThemeElement::StatusLineInactive: return "mode-line-inactive";
        case ThemeElement::MessageLine: return "minibuffer-prompt";
        case ThemeElement::LineNumber: return "line-number";
        case ThemeElement::Cursor: return "cursor";
        case ThemeElement::Selection: return "region";
        case ThemeElement::SearchMatch: return "isearch";
        case ThemeElement::SearchFail: return "isearch-fail";
        case ThemeElement::WindowBorder: return "window-divider";
        case ThemeElement::WindowBorderActive: return "window-divider-active";
        case ThemeElement::Background: return "default"; // Background usually part of default
        default: return "default";
    }
}

void Theme::set_face(const std::string& name, const FaceAttributes& attrs) {
    faces_[name] = attrs;
    // Invalidate cache
    face_pairs_.erase(name);
}

std::optional<FaceAttributes> Theme::get_face(const std::string& name) const {
    return get_face_recursive(name, 0);
}

std::optional<FaceAttributes> Theme::get_face_recursive(const std::string& name, int depth) const {
    // Prevent infinite recursion
    if (depth > 10) {
        return std::nullopt;
    }

    auto it = faces_.find(name);
    if (it == faces_.end()) {
        return std::nullopt;
    }

    FaceAttributes current = it->second;

    // Handle inheritance
    if (current.inherit) {
        auto parent = get_face_recursive(*current.inherit, depth + 1);
        if (parent) {
            // Start with parent attributes
            FaceAttributes final_attrs = *parent;
            // Merge current (child) attributes on top, overriding parent
            final_attrs.merge(current);
            return final_attrs;
        }
    }

    return current;
}

void Theme::set_color(ThemeElement element, const Color& fg, const Color& bg) {
    std::string face_name = element_to_face_name(element);
    FaceAttributes attrs;
    attrs.foreground = fg;
    attrs.background = bg;
    
    // Merge with existing if possible, or set new
    auto existing = get_face(face_name);
    if (existing) {
        FaceAttributes merged = *existing;
        merged.foreground = fg;
        merged.background = bg;
        set_face(face_name, merged);
    } else {
        set_face(face_name, attrs);
    }
}

Color Theme::get_fg_color(ThemeElement element) const {
    auto face = get_face(element_to_face_name(element));
    if (face && face->foreground) return *face->foreground;
    return Color(255, 255, 255);
}

Color Theme::get_bg_color(ThemeElement element) const {
    auto face = get_face(element_to_face_name(element));
    if (face && face->background) return *face->background;
    return Color(-1, -1, -1);
}

int Theme::get_color_pair(ThemeElement element) const {
    return get_face_color_pair(element_to_face_name(element));
}

int Theme::get_face_color_pair(const std::string& name) const {
    // Check cache
    auto it = face_pairs_.find(name);
    if (it != face_pairs_.end()) {
        return COLOR_PAIR(it->second);
    }
    
    // Get attributes
    auto face = get_face(name);
    
    // If face not found, try default
    if (!face && name != "default") {
        face = get_face("default");
    }
    
    if (!face) {
        return 0; // No attributes found
    }
    
    // Create new color pair
    int pair_id = next_pair_id_++;
    if (pair_id >= COLOR_PAIRS) {
        return 0; // Fallback to default if out of pairs
    }
    
    // Resolve colors (fallback to default if missing)
    Color fg = face->foreground.value_or(Color(255, 255, 255));
    Color bg = face->background.value_or(Color(-1, -1, -1));
    
    // Special case: if this is a face that should inherit from default, we might need logic here.
    // For now, just use what we have.
    
    int fg_color = fg.to_ncurses_color();
    int bg_color = bg.to_ncurses_color();
    
    init_pair(pair_id, fg_color, bg_color);
    face_pairs_[name] = pair_id;
    
    return COLOR_PAIR(pair_id);
}

int Theme::get_face_attributes_ncurses(const std::string& name) const {
    int pair = get_face_color_pair(name);
    int attrs = pair;
    
    auto face = get_face(name);
    if (!face && name != "default") {
        face = get_face("default");
    }
    
    if (face) {
        if (face->weight == FontWeight::Bold) attrs |= A_BOLD;
        // A_ITALIC might not be defined on all ncurses versions, use ifdef or ignore for now
        #ifdef A_ITALIC
        if (face->slant == FontSlant::Italic) attrs |= A_ITALIC;
        #endif
        if (face->underline.value_or(false)) attrs |= A_UNDERLINE;
        if (face->inverse.value_or(false)) attrs |= A_REVERSE;
    }
    
    return attrs;
}

void Theme::initialize_ncurses_colors() {
    // Initialize known standard faces
    std::vector<std::string> standard_faces = {
        "default", "font-lock-keyword-face", "font-lock-string-face", "font-lock-comment-face",
        "font-lock-function-name-face", "font-lock-type-face", "font-lock-constant-face",
        "error", "mode-line", "mode-line-inactive", "minibuffer-prompt", "line-number",
        "cursor", "region", "isearch", "isearch-fail", "window-divider"
    };
    
    for (const auto& name : standard_faces) {
        get_face_color_pair(name);
    }
}

// ... (ThemeManager implementation remains mostly same, but ensures we set faces correctly)

void ThemeManager::register_theme(std::shared_ptr<Theme> theme) {
    themes_[theme->name()] = theme;
    if (!active_theme_) {
        active_theme_ = theme;
    }
}

void ThemeManager::set_active_theme(const std::string& name) {
    auto it = themes_.find(name);
    if (it != themes_.end()) {
        active_theme_ = it->second;
        // The theme is already initialized (if necessary) when created by Lua
    }
}

std::vector<std::string> ThemeManager::theme_names() const {
    std::vector<std::string> names;
    for (auto& [name, theme] : themes_) {
        names.push_back(name);
    }
    return names;
}

// create_default_themes() and individual create_X_theme() methods are now removed.
// Themes will be loaded from Lua scripts.

} // namespace lumacs
