#pragma once

#include <library/cpp/config/sax.h>
#include <util/generic/hash_set.h>
#include <util/network/ip.h>
#include <util/network/socket.h>
#include <util/string/builder.h>
#include <balancer/kernel/net/address.h>

namespace NSrvKernel {
    class TAddr: public NConfig::IConfig::IFunc {
    public:
        TAddr(NConfig::IConfig* config)
                : Port_(80)
        {
            config->ForEach(this);
        }

        TNetworkAddress ToNetworkAddr() const {
            if (!Host_ || Host_ == "*") {
                return TNetworkAddress(Port_);
            }

            return TNetworkAddress(Host_, Port_);
        }

        const TString& Host() const noexcept {
            return Host_;
        }

        TIpPort Port() const noexcept {
            return Port_;
        }

        bool Disabled() const noexcept {
            return Disabled_;
        }

    private:
        START_PARSE {
                ON_KEY("ip", Host_) {
                    return;
                }

                ON_KEY("port", Port_) {
                    return;
                }

                ON_KEY("disabled", Disabled_) {
                    return;
                }

                if (key == "admin") { // backward comp
                    return;
                }
            } END_PARSE

    private:
        TString Host_;
        TIpPort Port_ = 0;
        bool Disabled_ = false;
    };

    class TConfigAddresses : public TVector<TNetworkAddress>, public NConfig::IConfig::IFunc {
    public:
        using TDisabledHosts = THashSet<TString>;

        const TDisabledHosts& DisabledHosts() const noexcept {
            return DisabledHosts_;
        }

        void ClearDisabledHosts() {
            DisabledHosts_.clear();
        }

    private:
        START_PARSE {
            TAddr addr(value->AsSubConfig());

            if (!value->IsContainer()) {
                ythrow NConfig::TConfigParseError() << "Expected an array. Did you forget {}?";
            }

            if (!addr.Disabled()) {
                if (addr.Host() == "*" || addr.Host().empty()) {
                    // see https://st.yandex-team.ru/BALANCER-650 https://st.yandex-team.ru/BALANCER-659 and address.cpp
                    ForEachNonLocalAddress(addr.Port(), [this] (TNetworkAddress address) {
                        this->push_back(address);
                    });
                } else {
                    push_back(addr.ToNetworkAddr());
                }
            } else {
                DisabledHosts_.insert(TStringBuilder() << addr.Host() << ":" << addr.Port());
            }

            return;
        } END_PARSE

    private:
        TDisabledHosts DisabledHosts_;
    };
}
