#include <balancer/modules/cookie_policy/ut_common/fuzz_common.h>
#include <balancer/modules/cookie_policy/policies/default_yandex_policies.h>

#include <util/generic/vector.h>
#include <util/generic/maybe.h>

extern "C" int LLVMFuzzerTestOneInput(const ui8* const wireData, const size_t wireSize) {
    using namespace NModCookiePolicy;
    TStringBuf wire((const char*)(wireData), wireSize);

    auto makeMod = [&](EYandexPolicyMode ymode, TMaybe<EPolicyMode> mode) {
        TModuleConfig cfg;
        cfg.set_uuid("xxx");
        cfg.set_default_yandex_policies(ymode);
        AddDefaultYandexPolicies(cfg);
        if (mode) {
            TModeControlsCfg ctl;
            ctl.policy_modes()["is_gdpr"].set_mode(*mode);
            ctl.policy_modes()["is_gdpr_b"].set_mode(*mode);
            ctl.policy_modes()["eprivacy_client"].set_mode(*mode);
            ctl.policy_modes()["eprivacy_lifetime"].set_mode(*mode);
            ctl.policy_modes()["eprivacy_server"].set_mode(*mode);
            ctl.policy_modes()["passport_protected"].set_mode(*mode);
            ctl.policy_modes()["yandex_ssn"].set_mode(*mode);
            ctl.policy_modes()["yandex_js"].set_mode(*mode);
            ctl.policy_modes()["yandex_sec"].set_mode(*mode);
            ctl.policy_modes()["yandex_root"].set_mode(*mode);
            ctl.policy_modes()["yandex_perm"].set_mode(*mode);
            ctl.policy_modes()["yandex_sess"].set_mode(*mode);
            ctl.policy_modes()["yandexuid_val"].set_mode(*mode);
            cfg.set_mode_controls(ctl);
        }
        return BuildModMock(cfg);
    };

    static const std::array<TModMock, 20> mocks {
        makeMod(EYandexPolicyMode::Off, Nothing()),
        makeMod(EYandexPolicyMode::Off, EPolicyMode::Off),
        makeMod(EYandexPolicyMode::Off, EPolicyMode::DryRun),
        makeMod(EYandexPolicyMode::Off, EPolicyMode::Fix),
        makeMod(EYandexPolicyMode::WatchOnly, Nothing()),
        makeMod(EYandexPolicyMode::WatchOnly, EPolicyMode::Off),
        makeMod(EYandexPolicyMode::WatchOnly, EPolicyMode::DryRun),
        makeMod(EYandexPolicyMode::WatchOnly, EPolicyMode::Fix),
        makeMod(EYandexPolicyMode::StableFix, Nothing()),
        makeMod(EYandexPolicyMode::StableFix, EPolicyMode::Off),
        makeMod(EYandexPolicyMode::StableFix, EPolicyMode::DryRun),
        makeMod(EYandexPolicyMode::StableFix, EPolicyMode::Fix),
        makeMod(EYandexPolicyMode::Unstable, Nothing()),
        makeMod(EYandexPolicyMode::Unstable, EPolicyMode::Off),
        makeMod(EYandexPolicyMode::Unstable, EPolicyMode::DryRun),
        makeMod(EYandexPolicyMode::Unstable, EPolicyMode::Fix),
        makeMod(EYandexPolicyMode::FixAll, Nothing()),
        makeMod(EYandexPolicyMode::FixAll, EPolicyMode::Off),
        makeMod(EYandexPolicyMode::FixAll, EPolicyMode::DryRun),
        makeMod(EYandexPolicyMode::FixAll, EPolicyMode::Fix),
    };

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

    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);

    RunFuzzTest(modMock, in);

    return 0;
}
