#pragma once

#include "WidgetConfig.hpp"
#include "widgets/Widget.hpp"
#include "lvgl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/portmacro.h"
#include <array>
#include <ctime>
#include <memory>

enum class KnxTimeType : uint8_t {
    TIME = 0,
    DATE = 1,
    DATETIME = 2,
};

class WidgetManager {
public:
    static WidgetManager& instance();

    // Initialize (load config from SD card)
    void init();

    // Build/rebuild all widgets from current config
    // Call this after loading or when user clicks "Save" in web editor
    void applyConfig();

    // Get current config for web editor (JSON)
    void getConfigJson(char* buf, size_t bufSize) const;

    // Update config from web editor (JSON) - does NOT apply immediately
    bool updateConfigFromJson(const char* json);

    // Save current config to SD card and apply to display
    void saveAndApply();

    // Reset to factory defaults
    void resetToDefaults();

    // Periodic tasks (standby handling)
    void loop();

    // User activity (resets standby timer)
    void onUserActivity();

    // Update widgets displaying system time
    void updateSystemTimeWidgets();

    // Thread-safe KNX updates (queued to UI thread)
    void onKnxValue(uint16_t groupAddr, float value, TextSource source);
    void onKnxValue(uint16_t groupAddr, float value);
    void onKnxSwitch(uint16_t groupAddr, bool value);
    void onKnxText(uint16_t groupAddr, const char* text);
    void onKnxTime(uint16_t groupAddr, const struct tm& value, KnxTimeType type);

    // Button action handler
    void handleButtonAction(const WidgetConfig& cfg, lv_obj_t* target);
    void goBack();

    // Navigation (for RoomCard sub-buttons etc.)
    void navigateToScreen(uint8_t screenId);
    void navigateBack();

    // KNX write (for RoomCard sub-buttons etc.)
    void sendKnxSwitch(uint16_t groupAddr, bool value);

    // Night mode
    bool isNightMode() const { return nightMode_; }

    // Direct config access
    GuiConfig& getConfig() { return *config_; }
    const GuiConfig& getConfig() const { return *config_; }
    const ScreenConfig* currentScreen() const;

private:
    WidgetManager();
    ~WidgetManager() = default;
    WidgetManager(const WidgetManager&) = delete;
    WidgetManager& operator=(const WidgetManager&) = delete;

    static constexpr size_t UI_EVENT_QUEUE_LEN = 16;
    static constexpr size_t UI_EVENT_TEXT_LEN = MAX_TEXT_LEN;

    enum class UiEventType : uint8_t {
        KNX_VALUE = 0,
        KNX_SWITCH = 1,
        KNX_TEXT = 2,
        KNX_TIME = 3,
    };

    struct UiEvent {
        UiEventType type;
        uint16_t groupAddr;
        TextSource textSource;
        float value;
        bool state;
        char text[UI_EVENT_TEXT_LEN];
        KnxTimeType timeType;
        struct tm timeValue;
    };

    static constexpr size_t KNX_CACHE_SIZE = MAX_WIDGETS * MAX_SCREENS;

    struct KnxNumericCacheEntry {
        uint16_t groupAddr = 0;
        TextSource source = TextSource::STATIC;
        float value = 0.0f;
        bool valid = false;
    };

    struct KnxSwitchCacheEntry {
        uint16_t groupAddr = 0;
        bool value = false;
        bool valid = false;
    };

    struct KnxTextCacheEntry {
        uint16_t groupAddr = 0;
        char text[MAX_TEXT_LEN] = {};
        bool valid = false;
    };

    struct KnxTimeCacheEntry {
        uint16_t groupAddr = 0;
        KnxTimeType type = KnxTimeType::TIME;
        struct tm value = {};
        bool valid = false;
    };

    void loadFromSdCard();
    void saveToSdCard();
    void destroyAllWidgets();
    void createAllWidgets(const ScreenConfig& screen, lv_obj_t* parent);
    bool enqueueUiEvent(const UiEvent& event);
    void processUiQueue();
    void applyCachedValuesToWidgets();
    void applyKnxValue(uint16_t groupAddr, float value, TextSource source);
    void applyKnxSwitch(uint16_t groupAddr, bool value);
    void applyKnxText(uint16_t groupAddr, const char* text);
    void applyKnxTime(uint16_t groupAddr, const struct tm& value, KnxTimeType type);
    void cacheKnxValue(uint16_t groupAddr, TextSource source, float value);
    void cacheKnxSwitch(uint16_t groupAddr, bool value);
    void cacheKnxText(uint16_t groupAddr, const char* text);
    void cacheKnxTime(uint16_t groupAddr, KnxTimeType type, const struct tm& value);
    bool getCachedKnxValue(uint16_t groupAddr, TextSource source, float* out) const;
    bool getCachedKnxSwitch(uint16_t groupAddr, bool* out) const;
    bool getCachedKnxText(uint16_t groupAddr, char* out, size_t outSize) const;
    bool getCachedKnxTime(uint16_t groupAddr, KnxTimeType type, struct tm* out) const;
    static bool isNumericTextSource(TextSource source);
    void refreshChartWidgetsLocked();
    void refreshChartWidgets();
    void applyNightMode(bool night);

    void createDefaultConfig();
    void applyScreen(uint8_t screenId);
    void applyScreenLocked(uint8_t screenId);
    void showScreenLocked(uint8_t screenId);
    void showModalScreen(const ScreenConfig& screen);
    void showModalScreenLocked(const ScreenConfig& screen);
    void closeModal();
    void closeModalLocked();
    void goBackLocked();
    void enterStandby();
    ScreenConfig* activeScreen();
    const ScreenConfig* activeScreen() const;

    static constexpr const char* CONFIG_FILE = "/sdcard/lvgl.json";
    static constexpr int64_t NAV_DELAY_US = 10 * 1000;  // 10ms delay (almost immediate)

    GuiConfig* config_ = nullptr;  // Allocated in PSRAM
    uint8_t activeScreenId_ = 0;
    uint8_t previousScreenId_ = 0xFF;
    uint8_t standbyReturnScreenId_ = 0xFF;
    uint8_t modalScreenId_ = 0xFF;
    bool standbyActive_ = false;
    bool standbyWakePending_ = false;
    uint8_t standbyWakeTarget_ = 0xFF;
    bool navPending_ = false;
    bool chartRefreshPending_ = false;
    bool nightMode_ = false;
    ButtonAction navAction_ = ButtonAction::KNX;
    uint8_t navTargetScreen_ = 0xFF;
    int64_t navRequestUs_ = 0;
    int64_t lastActivityUs_ = 0;
    int64_t lastSystemTimeUpdateUs_ = 0;

    // Runtime widget instances (indexed by widget ID)
    std::array<std::unique_ptr<Widget>, 256> widgets_;
    lv_obj_t* screen_ = nullptr;
    lv_obj_t* modalContainer_ = nullptr;
    lv_obj_t* modalDimmer_ = nullptr;
    QueueHandle_t uiQueue_ = nullptr;

    std::array<KnxNumericCacheEntry, KNX_CACHE_SIZE> knxNumericCache_ = {};
    std::array<KnxSwitchCacheEntry, KNX_CACHE_SIZE> knxSwitchCache_ = {};
    std::array<KnxTextCacheEntry, KNX_CACHE_SIZE> knxTextCache_ = {};
    std::array<KnxTimeCacheEntry, KNX_CACHE_SIZE> knxTimeCache_ = {};
    size_t knxNumericCacheNext_ = 0;
    size_t knxSwitchCacheNext_ = 0;
    size_t knxTextCacheNext_ = 0;
    size_t knxTimeCacheNext_ = 0;
    mutable portMUX_TYPE knxCacheMux_ = {};
};
