#pragma once

#include "WidgetConfig.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <array>
#include <climits>
#include <cstdint>
#include <ctime>

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

    void configureFromConfig(const GuiConfig& config);
    bool updateLatest(uint16_t groupAddr, TextSource source, float value);
    bool isTracked(uint16_t groupAddr, TextSource source) const;
    bool tick();
    void performAutoSave();

    bool fillChartSeries(uint16_t groupAddr, TextSource source, ChartPeriod period,
                         int32_t* outValues, size_t outCount) const;

    int64_t now() const;
    bool isTimeSynced() const { return now() > 1577836800LL; }

    void updateTimeOfDay(const tm& value);
    void updateDate(const tm& value);
    void updateDateTime(const tm& value);
    void clearAll();

    void loadFromSdCard();
    void saveToSdCard();

    static constexpr size_t CHART_POINT_COUNT = 120;
    static constexpr int32_t NO_POINT = INT32_MAX;

private:
    HistoryStore();
    ~HistoryStore();

    struct SeriesKey {
        uint16_t addr = 0;
        TextSource source = TextSource::STATIC;
    };

    struct HistoryPoint {
        int32_t ts = 0;
        float value = 0.0f;
    };

    template <size_t N>
    struct RingBuffer {
        std::array<HistoryPoint, N> points = {};
        size_t count = 0;
        size_t head = 0;

        void clear() {
            count = 0;
            head = 0;
        }

        void push(const HistoryPoint& point) {
            points[head] = point;
            head = (head + 1) % N;
            if (count < N) {
                count++;
            }
        }

        template <typename Fn>
        void forEach(Fn&& fn) const {
            if (count == 0) return;
            size_t start = (head + N - count) % N;
            for (size_t i = 0; i < count; ++i) {
                const HistoryPoint& p = points[(start + i) % N];
                fn(p);
            }
        }
    };

    static constexpr size_t HISTORY_MAX_SERIES = 4;
    static constexpr size_t HISTORY_FINE_CAP = 360;
    static constexpr size_t HISTORY_COARSE_CAP = 360;
    static constexpr int32_t HISTORY_FINE_INTERVAL = 120;
    static constexpr int32_t HISTORY_COARSE_INTERVAL = 3600;
    static constexpr int32_t HISTORY_MONTH_SECONDS = 30 * 24 * 3600;

    struct HistorySeries {
        SeriesKey key;
        bool active = false;
        bool hasLatest = false;
        float latestValue = 0.0f;
        int32_t latestTs = 0;
        RingBuffer<HISTORY_FINE_CAP> fine;
        RingBuffer<HISTORY_COARSE_CAP> coarse;
        int32_t lastFineSampleTs = 0;
        int32_t lastCoarseSampleTs = 0;
    };

    static bool isNumericSource(TextSource source);
    static bool keysEqual(const SeriesKey& a, const SeriesKey& b);

    int32_t periodSeconds(ChartPeriod period) const;

    HistorySeries* findSeries(uint16_t groupAddr, TextSource source);
    const HistorySeries* findSeries(uint16_t groupAddr, TextSource source) const;
    HistorySeries* findSeriesByKey(const SeriesKey& key);
    const HistorySeries* findSeriesByKey(const SeriesKey& key) const;

    std::array<HistorySeries, HISTORY_MAX_SERIES> series_ = {};
    size_t seriesCount_ = 0;

    bool dirty_ = false;
    int64_t lastSaveMonoUs_ = 0;

    mutable SemaphoreHandle_t mutex_ = nullptr;
};
