#pragma once

#include "common.h"
#include "response_matcher.h"
#include "request_matcher.h"

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


using namespace NConfig;

namespace NSrvKernel {

    template<class T>
    THolder<IMatcher<T>> ConstructMatcher(ICtl* ctl, const TString& key, NConfig::IConfig* config);

    template<class T>
    struct TAllMatcher final : public IMatcher<T> {
        bool Match(const T&) const noexcept override {
            return true;
        }

        bool NeedsHttp() const noexcept override {
            return false;
        }

        void Init(IWorkerCtl*) override {}
    };


    template<class T>
    class TSubMatchers final : public IConfig::IFunc {
    public:
        class TSlaveLoader final : public IConfig::IFunc {
        public:
            TSlaveLoader(ICtl* ctl, IConfig* config)
                : Ctl_(ctl)
            {
                config->ForEach(this);

                if (!Processed || !Matcher) {
                    ythrow TConfigParseError() << "Not found slave module";
                }
            }

            [[nodiscard]] bool NeedsHttp() const noexcept {
                return Matcher && Matcher->NeedsHttp();
            }

        private:
            START_PARSE {
                if (Processed) {
                    ythrow TConfigParseError() << "Found 2nd slave module <" << key << ">";
                }

                Matcher = ConstructMatcher<T>(Ctl_, key, value->AsSubConfig());
                Processed = true;

                return;
            } END_PARSE

        public:
            ICtl* Ctl_;
            THolder<IMatcher<T>> Matcher;
            bool Processed = false;
        };

    public:
        explicit TSubMatchers(ICtl* ctl, IConfig* config)
            : Ctl_(ctl)
        {
            config->ForEach(this);
        }

        [[nodiscard]] bool NeedsHttp() const noexcept {
            for (const auto& matcher : Slaves_) {
                if (matcher && matcher->NeedsHttp()) {
                    return true;
                }
            }
            return false;
        }

        void Init(IWorkerCtl* process) {
            for (auto& matcher : Slaves_) {
                matcher->Init(process);
            }
        }

        bool Empty() const noexcept {
            return Slaves_.empty();
        }

        const TVector<THolder<IMatcher<T>>>& Matchers() const noexcept {
            return Slaves_;
        }

    private:
        START_PARSE {
            Slaves_.push_back(TSlaveLoader(Ctl_, value->AsSubConfig()).Matcher);
            return;
        } END_PARSE

    private:
        ICtl* Ctl_;
        TVector<THolder<IMatcher<T>>> Slaves_;
    };


    template<class T>
    class TNotMatcher final : public IMatcher<T> {
    public:
        TNotMatcher(ICtl* ctl, IConfig* config) {
            typename TSubMatchers<T>::TSlaveLoader loader(ctl, config);
            Matcher_.Reset(loader.Matcher.Release());
            if (!Matcher_) {
                ythrow TConfigParseError() << "Not found submatcher for TNotMatcher";
            }
        }

        bool Match(const T& target) const noexcept override {
            return !Matcher_->Match(target);
        }

        bool NeedsHttp() const noexcept override {
            return Matcher_ && Matcher_->NeedsHttp();
        }

        void Init(IWorkerCtl* process) override {
            Matcher_->Init(process);
        }

    private:
        THolder<IMatcher<T>> Matcher_;
    };


    template<class T>
    class TAndMatcher final : public IMatcher<T> {
    public:
        TAndMatcher(ICtl* ctl, IConfig* config)
            : Slaves_(ctl, config)
        {
            if (Slaves_.Empty()) {
                ythrow TConfigParseError() << "no submatchers for \"and\" matcher";
            }
        }

        bool Match(const T& target) const noexcept override {
            for (const auto& i : Slaves_.Matchers()) {
                if (!i->Match(target)) {
                    return false;
                }
            }
            return true;
        }

        bool NeedsHttp() const noexcept override {
            return Slaves_.NeedsHttp();
        }

        void Init(IWorkerCtl* process) override {
            Slaves_.Init(process);
        }

    private:
        TSubMatchers<T> Slaves_;
    };


    template<class T>
    class TOrMatcher final : public IMatcher<T> {
    public:
        TOrMatcher(ICtl* ctl, IConfig* config)
            : Slaves_(ctl, config)
        {
            if (Slaves_.Empty()) {
                ythrow TConfigParseError() << "no submatchers for \"or\" matcher";
            }
        }

        bool Match(const T& target) const noexcept override {
            for (const auto& i : Slaves_.Matchers()) {
                if (i->Match(target)) {
                    return true;
                }
            }
            return false;
        }

        bool NeedsHttp() const noexcept override {
            return Slaves_.NeedsHttp();
        }

        void Init(IWorkerCtl* process) override {
            Slaves_.Init(process);
        }

    private:
        TSubMatchers<T> Slaves_;
    };

    template<class T>
    THolder<IMatcher<T>> ConstructMatcher(ICtl* ctl, const TString& key, NConfig::IConfig* config) {
        if (key == "match_all") {
            return MakeHolder<TAllMatcher<T>>();
        }

        if (key == "match_not") {
            return MakeHolder<TNotMatcher<T>>(ctl, config);
        }

        if (key == "match_and") {
            return MakeHolder<TAndMatcher<T>>(ctl, config);
        }

        if (key == "match_or") {
            return MakeHolder<TOrMatcher<T>>(ctl, config);
        }

        return ConstructSpecMatcher<T>(key, config, ctl);
    }

    THolder<IRequestMatcher> ConstructRequestMatcher(ICtl* ctl, const TString& key, NConfig::IConfig* config);

    THolder<IResponseMatcher> ConstructResponseMatcher(ICtl* ctl, const TString& key, NConfig::IConfig* config);
}
