#pragma once

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <deque>
#include <numeric>
#include <vector>

struct Rolling_Statistics_Snapshot {
    std::size_t sample_count{};
    double average{};
    double deviation{};
    double p50{};
    double p95{};
    double p99{};
    double minimum{};
    double maximum{};
};

class Rolling_Statistics final {
public:
    explicit Rolling_Statistics(std::size_t capacity) : capacity_(capacity) {}

    void add(double value) {
        if (!std::isfinite(value) || capacity_ == 0)
            return;
        samples_.push_back(value);
        while (samples_.size() > capacity_)
            samples_.pop_front();
    }

    void replace_latest(double value) {
        if (!std::isfinite(value) || samples_.empty())
            return;
        samples_.back() = value;
    }

    void clear() noexcept {
        samples_.clear();
    }

    [[nodiscard]] Rolling_Statistics_Snapshot snapshot() const {
        if (samples_.empty())
            return {};
        std::vector<double> sorted(samples_.begin(), samples_.end());
        std::ranges::sort(sorted);
        const double average =
            std::accumulate(sorted.begin(), sorted.end(), 0.0) /
            static_cast<double>(sorted.size());
        double squared_deviation{};
        for (const double sample : sorted) {
            const double difference = sample - average;
            squared_deviation += difference * difference;
        }
        const auto percentile = [&sorted](double ratio) {
            const auto rank = static_cast<std::size_t>(
                std::ceil(ratio * static_cast<double>(sorted.size())));
            return sorted[std::clamp<std::size_t>(rank, 1, sorted.size()) - 1];
        };
        return {
            sorted.size(),
            average,
            std::sqrt(squared_deviation / static_cast<double>(sorted.size())),
            percentile(0.50),
            percentile(0.95),
            percentile(0.99),
            sorted.front(),
            sorted.back()
        };
    }

private:
    std::size_t capacity_;
    std::deque<double> samples_;
};
