#pragma once

#include <balancer/kernel/module/conn_descr.h>

#include <util/generic/maybe.h>
#include <util/generic/xrange.h>

namespace NReport {
    namespace NLegacyRange {
        using namespace NSrvKernel;

        enum class EDim : unsigned {
            Status = 0,
            Robot,
            Internal,
            Ssl,
            Total
        };

        struct TDimFilter {
            unsigned Mask = 0;

        public:
            TDimFilter& SetAll() {
                Mask |= (TDimFilter().Set(EDim::Total).Mask - 1);
                return *this;
            }

            TDimFilter& Set(EDim dim) {
                Mask |= (1u << unsigned(dim));
                return *this;
            }

            TDimFilter& Reset(EDim dim) {
                Mask &= ~TDimFilter().Set(dim).Mask;
                return *this;
            }
        };


        struct TSslStatus {
            constexpr bool operator==(TSslStatus rhs) const noexcept {
                return UserConnIsSsl == rhs.UserConnIsSsl && ThisConnIsSsl == rhs.ThisConnIsSsl;
            };
            constexpr bool operator!=(TSslStatus rhs) const noexcept {
                return !operator==(rhs);
            }

            bool UserConnIsSsl = false;
            bool ThisConnIsSsl = false;
        };


        struct TDimData {
            unsigned Status = 0;
            TMaybe<bool> Robot;
            TMaybe<bool> Internal;
            TSslStatus Ssl;

        public:
            TDimData& SetStatus(unsigned sc) {
                Status = sc;
                return *this;
            }

            TDimData& SetRobot(TMaybe<bool> sr) {
                Robot = sr;
                return *this;
            }

            TDimData& SetInternal(TMaybe<bool> ir) {
                Internal = ir;
                return *this;
            }

            TDimData& SetSsl(TSslStatus sl) {
                Ssl = sl;
                return *this;
            }
        };

        size_t GetMatrixSize(TDimFilter filter);

        size_t GetUnistatMatrixSize(TDimFilter filter);

        size_t GetIndex(TDimFilter filter, const TDimData& data);

        size_t GetUnistatIndex(TDimFilter filter, TDimData data, bool disableRobotness);

        TStringBuf GetPrefix(TDimFilter filter, size_t index);

        TStringBuf GetSuffix();

        TString GetUnistatSuffix(TDimFilter filter, size_t index);

        TString GetDimParameter(TDimFilter filter, size_t index, EDim parameter);

        class TStorage {
        public:
            TStorage() = default;

            explicit TStorage(TDimFilter filter)
                : Filter(filter)
            {}

            bool Empty() const noexcept {
                return !Counters;
            }

            void Add(const TDimData& data) {
                if (!Counters) {
                    Counters.ConstructInPlace(GetMatrixSize(Filter));
                }
                Counters->at(GetIndex(Filter, data)) += 1;
            }

            size_t Get(const TDimData& data) const {
                return Counters ? Counters->at(GetIndex(Filter, data)) : 0;
            }

        private:
            TDimFilter Filter;
            TMaybe<TVector<size_t>> Counters;
        };


        TDimData GetDimData(const TConnDescr& descr, unsigned respStatus);

        void Initialize();
    }
}
