#pragma once

#include "common.h"

#include <balancer/kernel/module/iface.h>
#include <balancer/kernel/regexp/regexp_pire.h>
#include <balancer/kernel/http/parser/http.h>

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

#include <util/string/strip.h>

namespace NSrvKernel {

    using IResponseMatcher = IMatcher<TResponse>;

    class TResponseCodesMatcher final : public IResponseMatcher {
    public:
        TResponseCodesMatcher(IConfig* config) {
            config->ForEach(this);
            if (Codes_.empty()) {
                ythrow TConfigParseError() << "empty response codes matcher";
            }
        }

    private:
        START_PARSE {
            if (key == "codes") {
                ParseMap(value->AsSubConfig(), [this](const auto& key, auto* val) {
                    Y_UNUSED(key);
                    const ui32 code = FromString<ui32>(val->AsString());
                    if (code < 100 || code >= 600) {
                        ythrow TConfigParseError() << "bad response code: " << code;
                    }
                    Codes_.insert(code);
                });
                return;
            }
        } END_PARSE

        bool Match(const TResponse& response) const noexcept override {
            return Codes_.contains(response.ResponseLine().StatusCode);
        }

        NFH::TDenseHashSetStaticMarker<ui32, 0> Codes_;
    };

    class THeaderMatcher final : public IResponseMatcher {
    public:
        THeaderMatcher(IConfig* config) {
            config->ForEach(this);
            if (!Name_ || !Value_) {
                ythrow TConfigParseError() << "Empty name or value for header matcher: " << Name_ << ' ' << Value_;
            }

            NameFsm_ = MakeHolder<TFsm>(Name_, TFsm::TOptions().SetCaseInsensitive(true));
            ValueFsm_ = MakeHolder<TFsm>(Value_, TFsm::TOptions().SetCaseInsensitive(ValueCaseInsensitive_));
        }

    private:

        START_PARSE {
            ON_KEY("name", Name_) {
                StripInPlace(Name_);
                return;
            }
            ON_KEY("value", Value_) {
                StripInPlace(Value_);
                return;
            }
            ON_KEY("value_case_insensitive", ValueCaseInsensitive_) {
                return;
            }
        } END_PARSE

        bool Match(const TResponse& response) const noexcept override {
            auto value = response.Headers().GetFirstValue(*NameFsm_);
            return value && NSrvKernel::Match(*ValueFsm_, value);
        }

        THolder<TFsm> NameFsm_;
        THolder<TFsm> ValueFsm_;

        TString Name_;
        TString Value_;
        bool ValueCaseInsensitive_ = false;
    };
}
