// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "ui_schema.h"
#include "skin_manager.h"
#include "color_var_resolver.h"
#include "element_styles.h"
#include "../material/typography.h"
#include "../material/color_theme.h"
#include "../theme.h"
#include "../theme_loader.h"
#include "../effects/imgui_acrylic.h"
#include <toml++/toml.hpp>
#include <fstream>
#include <sstream>
#include <cstdio>
#include "../../util/logger.h"

namespace dragonx {
namespace ui {
namespace schema {

// ============================================================================
// Singleton
// ============================================================================

UISchema& UISchema::instance() {
    static UISchema s;
    return s;
}

// ============================================================================
// Load
// ============================================================================

bool UISchema::loadFromFile(const std::string& path) {
    toml::table root;
    try {
        root = toml::parse_file(path);
    } catch (const toml::parse_error& e) {
        DEBUG_LOGF("[UISchema] TOML parse error in %s: %s\n", path.c_str(), e.what());
        return false;
    }

    // Clear previous data
    elements_.clear();
    styleCache_.clear();
    backgroundImagePath_.clear();
    logoImagePath_.clear();

    // Parse top-level sections
    if (auto* theme = root["theme"].as_table()) {
        parseTheme(static_cast<const void*>(theme));
    }

    if (auto* bp = root["breakpoints"].as_table()) {
        parseBreakpoints(static_cast<const void*>(bp));
    }

    if (auto* globals = root["globals"].as_table()) {
        parseGlobals(static_cast<const void*>(globals));
    }

    // Parse tabs, dialogs, components sections → store elements
    if (auto* tabs = root["tabs"].as_table()) {
        parseSections(static_cast<const void*>(tabs), "tabs");
    }

    if (auto* dialogs = root["dialogs"].as_table()) {
        parseSections(static_cast<const void*>(dialogs), "dialogs");
    }

    if (auto* components = root["components"].as_table()) {
        parseSections(static_cast<const void*>(components), "components");
    }

    // Parse screens as a 3-level section (screens.loading, screens.first-run, etc.)
    if (auto* screens = root["screens"].as_table()) {
        parseSections(static_cast<const void*>(screens), "screens");
    }

    // Parse flat sections (2-level: sectionName.elementName → {style object})
    for (const auto& flatSection : {"business", "animations", "console",
            "backdrop", "shutdown", "notifications", "status-bar",
            "qr-code", "content-area", "style", "responsive",
            "spacing", "spacing-tokens", "button", "dialog", "input", "fonts",
            "inline-dialogs", "sidebar", "panels", "typography",
            "effects", "security"}) {
        if (auto* sec = root[flatSection].as_table()) {
            parseFlatSection(static_cast<const void*>(sec), flatSection);
        }
    }

    currentPath_ = path;
    if (basePath_.empty()) {
        basePath_ = path;     // Only set on first load (the true base)
    }
    overlayPath_.clear(); // No overlay yet
    loaded_ = true;
    dirty_ = false;
    ++generation_;

    // Record initial modification time
    try {
        lastModTime_ = std::filesystem::last_write_time(path);
        if (path == basePath_) {
            baseModTime_ = lastModTime_;
        }
    } catch (const std::filesystem::filesystem_error&) {
        // Non-fatal
    }

    DEBUG_LOGF("[UISchema] Loaded: %s (theme: %s, %s, %zu elements, gen=%u)\n",
           path.c_str(), themeName_.c_str(),
           darkTheme_ ? "dark" : "light",
           elements_.size(), generation_);

    return true;
}

bool UISchema::loadFromString(const std::string& tomlStr, const std::string& label) {
    toml::table root;
    try {
        root = toml::parse(tomlStr);
    } catch (const toml::parse_error& e) {
        DEBUG_LOGF("[UISchema] TOML parse error (%s): %s\n", label.c_str(), e.what());
        return false;
    }

    // Store the raw TOML string so the base can be reloaded later
    // (needed when no file-based basePath_ is available, e.g., Windows embedded)
    embeddedTomlStr_ = tomlStr;

    // Clear previous data
    elements_.clear();
    styleCache_.clear();
    backgroundImagePath_.clear();
    logoImagePath_.clear();

    // Parse top-level sections
    if (auto* theme = root["theme"].as_table()) {
        parseTheme(static_cast<const void*>(theme));
    }

    if (auto* bp = root["breakpoints"].as_table()) {
        parseBreakpoints(static_cast<const void*>(bp));
    }

    if (auto* globals = root["globals"].as_table()) {
        parseGlobals(static_cast<const void*>(globals));
    }

    if (auto* tabs = root["tabs"].as_table()) {
        parseSections(static_cast<const void*>(tabs), "tabs");
    }

    if (auto* dialogs = root["dialogs"].as_table()) {
        parseSections(static_cast<const void*>(dialogs), "dialogs");
    }

    if (auto* components = root["components"].as_table()) {
        parseSections(static_cast<const void*>(components), "components");
    }

    // Parse screens as a 3-level section (screens.loading, screens.first-run, etc.)
    if (auto* screens = root["screens"].as_table()) {
        parseSections(static_cast<const void*>(screens), "screens");
    }

    // Parse flat sections (2-level: sectionName.elementName → {style object})
    for (const auto& flatSection : {"business", "animations", "console",
            "backdrop", "shutdown", "notifications", "status-bar",
            "qr-code", "content-area", "style", "responsive",
            "spacing", "spacing-tokens", "button", "dialog", "input", "fonts",
            "inline-dialogs", "sidebar", "panels", "typography",
            "effects", "security"}) {
        if (auto* sec = root[flatSection].as_table()) {
            parseFlatSection(static_cast<const void*>(sec), flatSection);
        }
    }

    overlayPath_.clear();          // No overlay when loading a full base
    currentPath_ = label;           // Track what is loaded (for logging)
    loaded_ = true;
    dirty_ = false;
    ++generation_;

    DEBUG_LOGF("[UISchema] Loaded from %s (theme: %s, %s, %zu elements)\n",
           label.c_str(), themeName_.c_str(),
           darkTheme_ ? "dark" : "light",
           elements_.size());

    return true;
}

// ============================================================================
// Overlay merge — all sections (theme + layout + effects)
// ============================================================================

bool UISchema::mergeOverlayFromFile(const std::string& path) {
    if (!loaded_) {
        DEBUG_LOGF("[UISchema] mergeOverlay called before base load — falling back to full load\n");
        return loadFromFile(path);
    }

    std::ifstream file(path);
    if (!file.is_open()) {
        DEBUG_LOGF("[UISchema] Failed to open overlay: %s\n", path.c_str());
        return false;
    }

    toml::table root;
    try {
        root = toml::parse_file(path);
    } catch (const toml::parse_error& e) {
        DEBUG_LOGF("[UISchema] TOML parse error in overlay %s: %s\n", path.c_str(), e.what());
        return false;
    }

    // Merge theme section (palette, elevation, images, dark flag, name)
    if (auto* theme = root["theme"].as_table()) {
        parseTheme(static_cast<const void*>(theme));
    }

    // Merge breakpoints + globals
    if (auto* bp = root["breakpoints"].as_table()) {
        parseBreakpoints(static_cast<const void*>(bp));
    }
    if (auto* globals = root["globals"].as_table()) {
        parseGlobals(static_cast<const void*>(globals));
    }

    // Merge tabs, dialogs, components (3-level sections)
    if (auto* tabs = root["tabs"].as_table()) {
        parseSections(static_cast<const void*>(tabs), "tabs");
    }
    if (auto* dialogs = root["dialogs"].as_table()) {
        parseSections(static_cast<const void*>(dialogs), "dialogs");
    }
    if (auto* components = root["components"].as_table()) {
        parseSections(static_cast<const void*>(components), "components");
    }

    // Merge screens (3-level section)
    if (auto* screens = root["screens"].as_table()) {
        parseSections(static_cast<const void*>(screens), "screens");
    }

    // Merge all flat sections (2-level)
    for (const auto& flatSection : {"business", "animations", "console",
            "backdrop", "shutdown", "notifications", "status-bar",
            "qr-code", "content-area", "style", "responsive",
            "spacing", "spacing-tokens", "button", "dialog", "input", "fonts",
            "inline-dialogs", "sidebar", "panels", "typography",
            "effects", "security"}) {
        if (auto* sec = root[flatSection].as_table()) {
            parseFlatSection(static_cast<const void*>(sec), flatSection);
        }
    }

    overlayPath_ = path;
    // Track overlay file for hot-reload
    currentPath_ = path;
    ++generation_;
    styleCache_.clear();  // Invalidate after overlay merges new elements
    try {
        lastModTime_ = std::filesystem::last_write_time(path);
    } catch (const std::filesystem::filesystem_error&) {}

    DEBUG_LOGF("[UISchema] Merged overlay: %s (theme: %s, %s)\n",
           path.c_str(), themeName_.c_str(),
           darkTheme_ ? "dark" : "light");

    return true;
}

// ============================================================================
// Reload base theme (from file or embedded string)
// ============================================================================

bool UISchema::reloadBase() {
    // Prefer file-based reload when a basePath is available
    if (!basePath_.empty()) {
        return loadFromFile(basePath_);
    }
    // Fallback: reload from stored embedded string
    if (!embeddedTomlStr_.empty()) {
        return loadFromString(embeddedTomlStr_, "embedded-reload");
    }
    DEBUG_LOGF("[UISchema] reloadBase: no base path or embedded data available\n");
    return false;
}

// ============================================================================
// Theme parsing
// ============================================================================

void UISchema::parseTheme(const void* dataObj) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);

    if (auto name = t["name"].value<std::string>()) {
        themeName_ = *name;
    }
    if (auto dark = t["dark"].value<bool>()) {
        darkTheme_ = *dark;
    }

    // Parse palette → ColorVarResolver
    if (auto* palette = t["palette"].as_table()) {
        std::unordered_map<std::string, ImU32> paletteMap;
        for (auto&& [key, val] : *palette) {
            if (!val.is_string()) continue;
            ImU32 color = 0;
            std::string colorStr = *val.value<std::string>();

            // Palette values are direct hex or rgba — not var() references
            if (ColorVarResolver::parseHex(colorStr, color)) {
                paletteMap[std::string(key.str())] = color;
            } else if (ColorVarResolver::parseRgba(colorStr, color)) {
                paletteMap[std::string(key.str())] = color;
            } else if (colorStr == "transparent") {
                paletteMap[std::string(key.str())] = IM_COL32(0, 0, 0, 0);
            } else {
                DEBUG_LOGF("[UISchema] Warning: unparseable palette color '%s': %s\n",
                       std::string(key.str()).c_str(), colorStr.c_str());
            }
        }
        colorResolver_.setPalette(paletteMap);
    }

    // Parse elevation (optional)
    if (auto* elevation = t["elevation"].as_table()) {
        // Elevation colors go into the same palette
        auto paletteMap = colorResolver_.palette();
        for (auto&& [key, val] : *elevation) {
            if (!val.is_string()) continue;
            ImU32 color = 0;
            std::string colorStr = *val.value<std::string>();
            if (ColorVarResolver::parseHex(colorStr, color)) {
                paletteMap[std::string(key.str())] = color;
            }
        }
        colorResolver_.setPalette(paletteMap);
    }

    // Parse image overrides (resolved relative to theme directory by SkinManager)
    if (auto* images = t["images"].as_table()) {
        if (auto bg = (*images)["background_image"].value<std::string>()) {
            backgroundImagePath_ = *bg;
        }
        if (auto logo = (*images)["logo"].value<std::string>()) {
            logoImagePath_ = *logo;
        }
    }
}

// ============================================================================
// Globals parsing
// ============================================================================

void UISchema::parseGlobals(const void* dataObj) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);

    if (auto* btn = t["button"].as_table()) {
        detail::parseButtonStyle(static_cast<const void*>(btn), globalButton_);
    }
    if (auto* inp = t["input"].as_table()) {
        detail::parseInputStyle(static_cast<const void*>(inp), globalInput_);
    }
    if (auto* lbl = t["label"].as_table()) {
        detail::parseLabelStyle(static_cast<const void*>(lbl), globalLabel_);
    }
    if (auto* tbl = t["table"].as_table()) {
        detail::parseTableStyle(static_cast<const void*>(tbl), globalTable_);
    }
    if (auto* cb = t["checkbox"].as_table()) {
        detail::parseCheckboxStyle(static_cast<const void*>(cb), globalCheckbox_);
    }
    if (auto* cmb = t["combo"].as_table()) {
        detail::parseComboStyle(static_cast<const void*>(cmb), globalCombo_);
    }
    if (auto* sld = t["slider"].as_table()) {
        detail::parseSliderStyle(static_cast<const void*>(sld), globalSlider_);
    }
    if (auto* win = t["window"].as_table()) {
        detail::parseWindowStyle(static_cast<const void*>(win), globalWindow_);
    }
    if (auto* sep = t["separator"].as_table()) {
        detail::parseSeparatorStyle(static_cast<const void*>(sep), globalSeparator_);
    }
}

// ============================================================================
// Section parsing — store TOML tables for lazy access
// ============================================================================

void UISchema::parseSections(const void* dataObj, const std::string& prefix) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);

    for (auto&& [sectionName, sectionNode] : t) {
        auto* sectionTable = sectionNode.as_table();
        if (!sectionTable) continue;

        std::string sectionPath = prefix + "." + std::string(sectionName.str());

        for (auto&& [elemName, elemNode] : *sectionTable) {
            auto* elemTable = elemNode.as_table();
            if (!elemTable) continue;

            std::string key = sectionPath + "." + std::string(elemName.str());
            elements_[key] = StoredElement{ toml::table(*elemTable) };

            // Recurse into nested sub-sections (3rd level)
            // e.g., tabs.balance.classic.logo-opacity → stored as
            //        "tabs.balance.classic.logo-opacity"
            for (auto&& [innerName, innerNode] : *elemTable) {
                if (auto* innerTable = innerNode.as_table()) {
                    std::string innerKey = key + "." + std::string(innerName.str());
                    elements_[innerKey] = StoredElement{ toml::table(*innerTable) };
                }
            }
        }
    }
}

void UISchema::parseFlatSection(const void* dataObj, const std::string& prefix) {
    const toml::table& t = *static_cast<const toml::table*>(dataObj);

    for (auto&& [elemName, elemNode] : t) {
        std::string key = prefix + "." + std::string(elemName.str());

        if (auto* elemTable = elemNode.as_table()) {
            // Check if this is a leaf element (values are primitives)
            // or a nested sub-section (values are objects)
            bool hasNestedObjects = false;
            for (auto&& [innerName, innerNode] : *elemTable) {
                if (innerNode.is_table()) {
                    hasNestedObjects = true;
                    break;
                }
            }

            if (hasNestedObjects) {
                // Nested sub-section (e.g., inline-dialogs.about.{width,height,...})
                // Store each inner object as prefix.elemName.innerName
                for (auto&& [innerName, innerNode] : *elemTable) {
                    if (auto* innerTable = innerNode.as_table()) {
                        std::string innerKey = key + "." + std::string(innerName.str());
                        elements_[innerKey] = StoredElement{ toml::table(*innerTable) };
                    }
                }
                // Also store the sub-section itself as a flat element
                elements_[key] = StoredElement{ toml::table(*elemTable) };
            } else {
                // Leaf element (e.g., business.block-reward: {"size": 1.5625})
                elements_[key] = StoredElement{ toml::table(*elemTable) };
            }
        } else if (elemNode.is_integer() || elemNode.is_floating_point()) {
            // Auto-wrap scalar number → {size = value}
            toml::table wrapped;
            wrapped.insert("size", elemNode.value<double>().value_or(0.0));
            elements_[key] = StoredElement{ std::move(wrapped) };
        } else if (elemNode.is_string()) {
            // Auto-wrap string → {font = value} or {color = value}
            std::string s = *elemNode.value<std::string>();
            toml::table wrapped;
            if (s.find("var(") == 0 || s.find("#") == 0 || s.find("rgba") == 0) {
                wrapped.insert("color", s);
            } else {
                wrapped.insert("font", s);
            }
            elements_[key] = StoredElement{ std::move(wrapped) };
        } else if (auto* arr = elemNode.as_array(); arr && arr->size() >= 2) {
            // Auto-wrap [x, y] array → {width = x, height = y}
            toml::table wrapped;
            wrapped.insert("width", (*arr)[0].value<double>().value_or(0.0));
            wrapped.insert("height", (*arr)[1].value<double>().value_or(0.0));
            elements_[key] = StoredElement{ std::move(wrapped) };
        }
    }
}

// ============================================================================
// Breakpoint parsing
// ============================================================================

void UISchema::parseBreakpoints(const void* jsonObj) {
    detail::parseBreakpointConfig(jsonObj, breakpoints_);
}

// ============================================================================
// Element lookup — find stored TOML table by key
// ============================================================================

const void* UISchema::findElement(const std::string& section, const std::string& name) const {
    std::string key = section + "." + name;
    auto it = elements_.find(key);
    if (it == elements_.end()) {
        return nullptr;
    }
    // Return pointer to the stored toml::table via std::any_cast
    return std::any_cast<toml::table>(&it->second.data);
}

// ============================================================================
// Hot-reload
// ============================================================================

void UISchema::pollForChanges() {
    if (!loaded_ || currentPath_.empty()) return;

    double now = ImGui::GetTime();
    if (now - lastPollTime_ < pollInterval_) return;
    lastPollTime_ = now;

    try {
        auto mtime = std::filesystem::last_write_time(currentPath_);
        if (mtime != lastModTime_) {
            lastModTime_ = mtime;
            dirty_ = true;
        }
        // Also check base file when an overlay is active
        if (!dirty_ && !overlayPath_.empty() && !basePath_.empty() && basePath_ != currentPath_) {
            auto btime = std::filesystem::last_write_time(basePath_);
            if (btime != baseModTime_) {
                baseModTime_ = btime;
                dirty_ = true;
            }
        }
    } catch (const std::filesystem::filesystem_error&) {
        // File might be mid-write — ignore
    }
}

void UISchema::applyIfDirty() {
    if (!dirty_) return;
    dirty_ = false;

    // Clear style cache before snapshot so we re-read from TOML
    styleCache_.clear();

    // Snapshot font sizes before reload for change detection
    static const char* fontKeys[] = {
        "h1", "h2", "h3", "h4", "h5", "h6",
        "subtitle1", "subtitle2", "body1", "body2",
        "button", "button-sm", "button-lg",
        "caption", "overline", "scale"
    };
    float prevFonts[16];
    for (int i = 0; i < 16; ++i) {
        prevFonts[i] = drawElement("fonts", fontKeys[i]).size;
    }

    // Snapshot image paths before reload for change detection
    std::string prevBgImage = backgroundImagePath_;
    std::string prevLogoImage = logoImagePath_;

    DEBUG_LOGF("[UISchema] Hot-reload: re-parsing %s\n", currentPath_.c_str());

    // Save overlay path before reloading — loadFromFile/loadFromString clear it
    std::string savedOverlay = overlayPath_;

    // If an overlay is active, reload base first then re-merge overlay
    if (!savedOverlay.empty() && !basePath_.empty()) {
        loadFromFile(basePath_);
        mergeOverlayFromFile(savedOverlay);
    } else if (!savedOverlay.empty() && !embeddedTomlStr_.empty()) {
        // Embedded base (e.g. Windows single-file): reload from stored string
        loadFromString(embeddedTomlStr_, "embedded-reload");
        mergeOverlayFromFile(savedOverlay);
    } else {
        loadFromFile(currentPath_);
    }

    // Detect font size changes
    for (int i = 0; i < 16; ++i) {
        float cur = drawElement("fonts", fontKeys[i]).size;
        if (cur != prevFonts[i]) {
            fonts_changed_ = true;
            DEBUG_LOGF("[UISchema] Font sizes changed, atlas rebuild needed\n");
            break;
        }
    }

    // Re-apply Material Design colors to ImGui from the reloaded palette
    reapplyColorsToImGui();

    // Detect image path changes and update SkinManager + trigger reload
    if (backgroundImagePath_ != prevBgImage || logoImagePath_ != prevLogoImage) {
        DEBUG_LOGF("[UISchema] Hot-reload: image paths changed (bg: '%s' → '%s', logo: '%s' → '%s')\n",
               prevBgImage.c_str(), backgroundImagePath_.c_str(),
               prevLogoImage.c_str(), logoImagePath_.c_str());

        auto& skinMgr = SkinManager::instance();
        std::string activeId = skinMgr.activeSkinId();
        if (!activeId.empty()) {
            // Re-resolve image paths from the new filenames
            skinMgr.resolveAndReloadImages(activeId, currentPath_);
        }
    }
}

// ============================================================================
// Color resolution
// ============================================================================

ImU32 UISchema::resolveColor(const std::string& ref, ImU32 fallback) const {
    return colorResolver_.resolve(ref, fallback);
}

void UISchema::reapplyColorsToImGui() {
    // Build a ColorTheme from the current palette
    const auto& pal = colorResolver_.palette();

    auto get = [&](const std::string& key, ImU32 fallback = 0) -> ImU32 {
        auto it = pal.find(key);
        return it != pal.end() ? it->second : fallback;
    };

    material::ColorTheme theme{};
    theme.primary          = get("--primary");
    theme.primaryVariant   = get("--primary-variant");
    theme.primaryLight     = get("--primary-light");
    theme.secondary        = get("--secondary");
    theme.secondaryVariant = get("--secondary-variant");
    theme.secondaryLight   = get("--secondary-light");
    theme.background       = get("--background");
    theme.surface          = get("--surface");
    theme.surfaceVariant   = get("--surface-variant");
    theme.onPrimary        = get("--on-primary");
    theme.onSecondary      = get("--on-secondary");
    theme.onBackground     = get("--on-background");
    theme.onSurface        = get("--on-surface");
    theme.onSurfaceMedium  = get("--on-surface-medium");
    theme.onSurfaceDisabled= get("--on-surface-disabled");
    theme.error            = get("--error");
    theme.onError          = get("--on-error");
    theme.success          = get("--success");
    theme.onSuccess        = get("--on-success");
    theme.warning          = get("--warning");
    theme.onWarning        = get("--on-warning");
    theme.divider          = get("--divider");
    theme.outline          = get("--outline");
    theme.scrim            = get("--scrim");

    // Fill missing fields with defaults
    ThemeLoader::computeDefaults(theme, darkTheme_);

    // Apply to ImGui and update acrylic theme
    material::ApplyColorThemeToImGui(theme);
    SetCurrentAcrylicTheme(ThemeLoader::deriveAcrylicTheme(theme));

    // Background colors changed — re-capture on next frame
    effects::ImGuiAcrylic::InvalidateCapture();

    DEBUG_LOGF("[UISchema] Hot-reload: re-applied colors to ImGui\n");
}

// ============================================================================
// Font resolution
// ============================================================================

ImFont* UISchema::resolveFont(const std::string& fontName) const {
    if (fontName.empty()) return nullptr;

    auto& typo = material::Typography::instance();
    if (!typo.isLoaded()) return nullptr;

    // Match font name strings to Typography accessors
    if (fontName == "h1")         return typo.h1();
    if (fontName == "h2")         return typo.h2();
    if (fontName == "h3")         return typo.h3();
    if (fontName == "h4")         return typo.h4();
    if (fontName == "h5")         return typo.h5();
    if (fontName == "h6")         return typo.h6();
    if (fontName == "subtitle1")  return typo.subtitle1();
    if (fontName == "subtitle2")  return typo.subtitle2();
    if (fontName == "body1")      return typo.body1();
    if (fontName == "body2")      return typo.body2();
    if (fontName == "button")     return typo.button();
    if (fontName == "button-sm")  return typo.buttonSm();
    if (fontName == "button-lg")  return typo.buttonLg();
    if (fontName == "caption")    return typo.caption();
    if (fontName == "overline")   return typo.overline();

    DEBUG_LOGF("[UISchema] Warning: unknown font name '%s'\n", fontName.c_str());
    return nullptr;
}

// ============================================================================
// Responsive breakpoint
// ============================================================================

Breakpoint UISchema::currentBreakpoint() const {
    ImVec2 size = ImGui::GetMainViewport()->Size;

    // Check compact (must satisfy ALL specified constraints)
    const auto& c = breakpoints_.compact;
    bool isCompact = false;
    if (c.maxWidth > 0 || c.maxHeight > 0) {
        isCompact = true;
        if (c.maxWidth > 0 && size.x > c.maxWidth)   isCompact = false;
        if (c.maxHeight > 0 && size.y > c.maxHeight)  isCompact = false;
    }
    if (isCompact) return Breakpoint::Compact;

    // Check expanded
    const auto& e = breakpoints_.expanded;
    bool isExpanded = false;
    if (e.minWidth > 0 || e.minHeight > 0) {
        isExpanded = true;
        if (e.minWidth > 0 && size.x < e.minWidth)    isExpanded = false;
        if (e.minHeight > 0 && size.y < e.minHeight)   isExpanded = false;
    }
    if (isExpanded) return Breakpoint::Expanded;

    return Breakpoint::Normal;
}

// ============================================================================
// Element lookups with merge
// ============================================================================

ButtonStyle UISchema::button(const std::string& section, const std::string& name) const {
    // Start with global defaults
    ButtonStyle result = globalButton_;

    // Overlay section-specific values
    const void* elem = findElement(section, name);
    if (elem) {
        ButtonStyle sectionStyle;
        detail::parseButtonStyle(elem, sectionStyle);
        mergeButton(result, sectionStyle);

        // Check for responsive overrides
        const toml::table& t = *static_cast<const toml::table*>(elem);
        Breakpoint bp = currentBreakpoint();
        if (bp == Breakpoint::Compact) {
            if (auto* compactTable = t["@compact"].as_table()) {
                ResponsiveButtonOverride ovr;
                detail::parseResponsiveButtonOverride(static_cast<const void*>(compactTable), ovr);
                applyResponsiveButton(result, ovr);
            }
        } else if (bp == Breakpoint::Expanded) {
            if (auto* expandedTable = t["@expanded"].as_table()) {
                ResponsiveButtonOverride ovr;
                detail::parseResponsiveButtonOverride(static_cast<const void*>(expandedTable), ovr);
                applyResponsiveButton(result, ovr);
            }
        }
    }

    return result;
}

InputStyle UISchema::input(const std::string& section, const std::string& name) const {
    InputStyle result = globalInput_;

    const void* elem = findElement(section, name);
    if (elem) {
        InputStyle sectionStyle;
        detail::parseInputStyle(elem, sectionStyle);
        mergeInput(result, sectionStyle);
    }

    return result;
}

LabelStyle UISchema::label(const std::string& section, const std::string& name) const {
    LabelStyle result = globalLabel_;

    const void* elem = findElement(section, name);
    if (elem) {
        LabelStyle sectionStyle;
        detail::parseLabelStyle(elem, sectionStyle);
        mergeLabel(result, sectionStyle);
    }

    return result;
}

TableStyle UISchema::table(const std::string& section, const std::string& name) const {
    TableStyle result = globalTable_;

    const void* elem = findElement(section, name);
    if (elem) {
        TableStyle sectionStyle;
        detail::parseTableStyle(elem, sectionStyle);

        // Merge table fields
        if (sectionStyle.minHeight >= 0)      result.minHeight = sectionStyle.minHeight;
        if (sectionStyle.heightRatio >= 0)    result.heightRatio = sectionStyle.heightRatio;
        if (sectionStyle.bottomReserve >= 0)  result.bottomReserve = sectionStyle.bottomReserve;
        if (sectionStyle.rowHeight >= 0)      result.rowHeight = sectionStyle.rowHeight;
        if (!sectionStyle.headerFont.empty()) result.headerFont = sectionStyle.headerFont;
        if (!sectionStyle.cellFont.empty())   result.cellFont = sectionStyle.cellFont;
        if (!sectionStyle.borderColor.empty()) result.borderColor = sectionStyle.borderColor;
        if (!sectionStyle.stripeColor.empty()) result.stripeColor = sectionStyle.stripeColor;
        // Columns: section columns override/extend global columns
        for (auto& [colName, colStyle] : sectionStyle.columns) {
            result.columns[colName] = colStyle;
        }
    }

    return result;
}

CheckboxStyle UISchema::checkbox(const std::string& section, const std::string& name) const {
    CheckboxStyle result = globalCheckbox_;

    const void* elem = findElement(section, name);
    if (elem) {
        CheckboxStyle sectionStyle;
        detail::parseCheckboxStyle(elem, sectionStyle);
        if (!sectionStyle.font.empty())       result.font = sectionStyle.font;
        if (!sectionStyle.color.empty())       result.color = sectionStyle.color;
        if (!sectionStyle.checkColor.empty())  result.checkColor = sectionStyle.checkColor;
        if (!sectionStyle.background.empty())  result.background = sectionStyle.background;
    }

    return result;
}

ComboStyle UISchema::combo(const std::string& section, const std::string& name) const {
    ComboStyle result = globalCombo_;

    const void* elem = findElement(section, name);
    if (elem) {
        ComboStyle sectionStyle;
        detail::parseComboStyle(elem, sectionStyle);
        if (sectionStyle.width > 0)             result.width = sectionStyle.width;
        if (!sectionStyle.font.empty())         result.font = sectionStyle.font;
        if (!sectionStyle.color.empty())        result.color = sectionStyle.color;
        if (!sectionStyle.background.empty())   result.background = sectionStyle.background;
        if (sectionStyle.borderRadius >= 0)     result.borderRadius = sectionStyle.borderRadius;
        if (sectionStyle.truncate > 0)          result.truncate = sectionStyle.truncate;
    }

    return result;
}

SliderStyle UISchema::slider(const std::string& section, const std::string& name) const {
    SliderStyle result = globalSlider_;

    const void* elem = findElement(section, name);
    if (elem) {
        SliderStyle sectionStyle;
        detail::parseSliderStyle(elem, sectionStyle);
        if (sectionStyle.width > 0)             result.width = sectionStyle.width;
        if (!sectionStyle.trackColor.empty())   result.trackColor = sectionStyle.trackColor;
        if (!sectionStyle.fillColor.empty())     result.fillColor = sectionStyle.fillColor;
        if (!sectionStyle.thumbColor.empty())    result.thumbColor = sectionStyle.thumbColor;
        if (sectionStyle.thumbRadius >= 0)       result.thumbRadius = sectionStyle.thumbRadius;
    }

    return result;
}

WindowStyle UISchema::window(const std::string& section, const std::string& name) const {
    WindowStyle result = globalWindow_;

    const void* elem = findElement(section, name);
    if (elem) {
        WindowStyle sectionStyle;
        detail::parseWindowStyle(elem, sectionStyle);
        mergeWindow(result, sectionStyle);
    }

    return result;
}

SeparatorStyle UISchema::separator(const std::string& section, const std::string& name) const {
    SeparatorStyle result = globalSeparator_;

    const void* elem = findElement(section, name);
    if (elem) {
        SeparatorStyle sectionStyle;
        detail::parseSeparatorStyle(elem, sectionStyle);
        if (!sectionStyle.color.empty())    result.color = sectionStyle.color;
        if (sectionStyle.thickness >= 0)    result.thickness = sectionStyle.thickness;
        if (sectionStyle.margin[0] > 0 || sectionStyle.margin[1] > 0) {
            result.margin[0] = sectionStyle.margin[0];
            result.margin[1] = sectionStyle.margin[1];
        }
    }

    return result;
}

const DrawElementStyle& UISchema::drawElement(const std::string& section, const std::string& name) const {
    static const DrawElementStyle s_empty{};

    std::string key = section + "." + name;

    // Return from cache if already parsed
    auto cit = styleCache_.find(key);
    if (cit != styleCache_.end()) {
        return cit->second;
    }

    // Parse from TOML and cache
    const void* elem = findElement(section, name);
    if (!elem) {
        return s_empty;
    }

    auto [it, _] = styleCache_.emplace(std::move(key), DrawElementStyle{});
    detail::parseDrawElementStyle(elem, it->second);
    return it->second;
}

} // namespace schema
} // namespace ui
} // namespace dragonx
