#pragma once

#include "ut_common.h"

#include <balancer/modules/cookie_policy/common/combined_policy.h>

#include <balancer/kernel/testing/process_mock.h>
#include <balancer/kernel/testing/stats.h>

namespace NModCookiePolicy {

    struct TModMock {
        THolder<TCombinedPolicy> Policy;
        THolder<TStatsFixture> Stats;
        THolder<TCombinedPolicyTls> TlsTemplate;
        THolder<TProcessMock> Proc;
        THolder<TCombinedPolicyTls> Tls;
    };

    TModMock BuildModMock(const TModuleConfig& cfg);

    void RunFuzzTest(const TModMock& mock, const TCPTestInput& c);

    template <class TGetPolicy>
    void FillAndTestCookiePolicy(TGetPolicy&& p, TStringBuf wire) {
        auto makeMod = [&](EPolicyMode mode, TMaybe<EPolicyMode> over) {
            TModuleConfig cfg;
            cfg.set_uuid("xxx");
            auto& pol = p(cfg);
            pol["yyy"].set_mode(mode);
            if (over) {
                TModeControlsCfg ctl;
                ctl.policy_modes()["yyy"].set_mode(over);
                cfg.set_mode_controls(ctl);
            }
            return BuildModMock(cfg);
        };

        static const std::array<TModMock, 8> mocks {
            makeMod(EPolicyMode::DryRun, EPolicyMode::Fix),
            makeMod(EPolicyMode::DryRun, EPolicyMode::DryRun),
            makeMod(EPolicyMode::DryRun, EPolicyMode::Off),
            makeMod(EPolicyMode::DryRun, Nothing()),
            makeMod(EPolicyMode::Fix, EPolicyMode::Fix),
            makeMod(EPolicyMode::Fix, EPolicyMode::DryRun),
            makeMod(EPolicyMode::Fix, EPolicyMode::Off),
            makeMod(EPolicyMode::Fix, Nothing()),
        };

        TCPTestInput in;
        in.Now = TInstant::Seconds(1600946358);

        if (CutUi8(wire)) {
            in.GeoCache.XIpProperties.ConstructInPlace();
            in.GeoCache.XIpProperties->IsGdpr = (CutUi8(wire) % 2);
            in.GeoCache.XIpProperties->IsVpn = (CutUi8(wire) % 2);
        }

        if (CutUi8(wire)) {
            in.GeoCache.XYandexEURequest = (CutUi8(wire) % 2);
        }

        in.XYandexEURequest = (CutUi8(wire) % 2) ? TStrVec{CutStr(wire)} : TStrVec{};
        in.XIpProperties = (CutUi8(wire) % 2) ? TStrVec{CutStr(wire)} : TStrVec{};

        static const std::array<TStrVec, 3> uas {
            TStrVec{}, TStrVec{TString(ChromimUA),}, TStrVec{TString(FirefoxUA),},
        };

        static const std::array<TStrVec, 6> hosts {
            TStrVec{"yandex.ru"}, TStrVec{"www.yandex.ru"},
            TStrVec{"yandex.eu"}, TStrVec{"www.yandex.eu"},
            TStrVec{}, TStrVec{"localhost"},
        };

        in.UserAgent = uas[CutUi8(wire) % uas.size()];
        in.Host = hosts[CutUi8(wire) % hosts.size()];

        in.Path = CutStr(wire, "/");
        in.Cookie = CutStrVec(wire);
        in.SetCookie = CutStrVec(wire);

        const auto& modMock = mocks[CutUi8(wire) % mocks.size()];
        RunFuzzTest(modMock, in);
    }
}
