#pragma once

#include <library/cpp/config/sax.h>
#include <library/cpp/config/config.h>
#include <library/cpp/containers/flat_hash/flat_hash.h>

#include <util/generic/strbuf.h>
#include <util/generic/string.h>
#include <util/string/cast.h>


namespace NSrvKernel {
    class TStatusCodeReaction {
    private:
        explicit TStatusCodeReaction(bool bad) noexcept
            : Bad_(bad)
        {}

    public:
        TStatusCodeReaction() noexcept = default;

        TStatusCodeReaction(const TStatusCodeReaction& rhs) noexcept = default;
        TStatusCodeReaction& operator=(const TStatusCodeReaction& rhs) noexcept = default;

        static TStatusCodeReaction Bad() noexcept {
            return TStatusCodeReaction(true);
        }

        static TStatusCodeReaction Good() noexcept {
            return TStatusCodeReaction(false);
        }


        bool IsBad() const noexcept {
            return Bad_;
        }

    private:
        bool Bad_{ false };
    };

    class TStatusCodeReactions {
    public:
        void SetUnknownIsBad(bool value) {
            UnknownIsBad_ = value;
        }

        void AddSpecialReaction(ui32 status, TStatusCodeReaction reaction) {
            SpecialReactions_[status] = reaction;
        }

        void AddClassReaction(ui32 statusClass, TStatusCodeReaction reaction) {
            ClassReactions_[statusClass] = reaction;
        }

        bool IsBad(ui32 status) const noexcept {
            auto concreteReaction = SpecialReactions_.find(status);
            if (concreteReaction != SpecialReactions_.end()) {
                return concreteReaction->second.IsBad();
            }

            auto statusCodeClass = status / 100;
            auto classReaction = ClassReactions_.find(statusCodeClass);
            if (classReaction != ClassReactions_.end()) {
                return classReaction->second.IsBad();
            }

            return UnknownIsBad_;
        }

        bool IsClassHasBadMembers(ui32 statusCodeClass) {
            auto classReaction = ClassReactions_.find(statusCodeClass);
            if (classReaction != ClassReactions_.end() && classReaction->second.IsBad()) {
                return true;
            }
            for (const auto& i : SpecialReactions_) {
                if (100 * statusCodeClass <= i.first && i.first < 100 * (statusCodeClass + 1) && i.second.IsBad()) {
                    return true;
                }
            }
            return false;
        }

    private:
        NFH::TFlatHashMap<ui32, TStatusCodeReaction> SpecialReactions_;
        NFH::TFlatHashMap<ui32, TStatusCodeReaction> ClassReactions_;
        bool UnknownIsBad_{ false };
    };

    class TStatusCodeReactionParser : public NConfig::IConfig::IFunc {
    public:
        TStatusCodeReactionParser(TStatusCodeReactions& collection, TStatusCodeReaction reaction)
            : Collection_(collection)
            , Reaction_(reaction)
        {}

    private:
        void DoConsume(const TString& key, NConfig::IConfig::IValue* value) override {
            Y_UNUSED(key);

            TString valueString = value->AsString();

            TStringBuf valueStrBuf(valueString);
            TStringBuf classBuf, emptyBuf;

            if (valueStrBuf.TryRSplit("xx", classBuf, emptyBuf)) { // like 5xx
                if (!emptyBuf.empty()) {
                    ythrow NConfig::TConfigParseError() << "bad status code value: " << valueString.Quote();
                }

                ui32 statusCodeClass = FromString<ui32>(classBuf);
                if (statusCodeClass >= 6) {
                    ythrow NConfig::TConfigParseError() << "bad status code value: " << valueString.Quote();
                }

                Collection_.AddClassReaction(statusCodeClass, Reaction_);
            } else {
                ui32 statusCode = FromString<ui32>(valueString);
                Collection_.AddSpecialReaction(statusCode, Reaction_);
            }
        }
    private:
        TStatusCodeReactions& Collection_;
        TStatusCodeReaction Reaction_;
    };
}
