#include <balancer/modules/cookie_policy/ut_common/ut_common.h>
#include <balancer/kernel/helpers/yuid.h>

#include <library/cpp/testing/unittest/registar.h>

using namespace NModCookiePolicy;

Y_UNIT_TEST_SUITE(TPolicyYandexuidValueTest) {

    namespace {
        struct TPolicyConfig {
            bool AllowChange = false;
            bool Fix = false;
        };

        auto MakeConfig(TPolicyConfig cfg) {
            return MakeCombinedPolicy(Sprintf(R"=(instance={
                    uuid="xxx";
                    yandexuid_value={
                        yyy={
                            allow_change=%u;
                            %s
                        };
                    };
                })=",
                (ui32)cfg.AllowChange,
                (cfg.Fix ? "mode='fix';" : "")
            ));
        }

        auto MakeCreateConfig() {
            return MakeCombinedPolicy(R"=(instance={
                    uuid="xxx";
                    yandexuid_value={
                        yyy={
                            allow_change=0;
                            mode='fix';
                        };
                    };
                    yandexuid_create={
                        zzz={
                            mode='fix';
                        };
                    };
                })="
            );
        }
    }

    Y_UNIT_TEST(TestDeletion) {
        const auto now = TInstant::Seconds(1596470989);

        const auto cc = ToCookie({
            TSetCookie{.Name="yandexuid", .MaxAge=0},
            TSetCookie{.Name="yandexuid", .Expires=TExpires::Past()},
            TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("4000000000000000000")},
            TSetCookie{.Name="other", .MaxAge=0},
        });

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .SetCookie=cc,
            },
            .Out={
                .SetCookie=ToCookie({
                    TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("4000000000000000000")},
                    TSetCookie{.Name="other", .MaxAge=0},
                }),
                .RespLog=" [cpol u:xxx-yyy fail+fix yandexuid=Drop:1!Deletion, yandexuid=Drop:1!Deletion,]",
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-modified-resp_summ", 1},
                    {"cpol-xxx-yyy-cookie-fail_summ", 2},
                    {"cpol-xxx-yyy-cookie-drop_summ", 2},
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 4},
                    {"cpol-xxx-yyy-fail_summ", 1},
                    {"cpol-xxx-yyy-fix_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 4),
            },
        }));

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=true, .Fix=true}),
                .Now=now,
                .SetCookie=cc,
            },
            .Out={
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 3},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 4},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 4),
            },
        }));
    }

    Y_UNIT_TEST(TestDeletionDryRun) {
        const auto now = TInstant::Seconds(1596470989);

        const auto cc = ToCookie({
            TSetCookie{.Name="yandexuid", .MaxAge=0},
            TSetCookie{.Name="yandexuid", .Expires=TExpires::Past()},
            TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("4000000000000000000")},
            TSetCookie{.Name="other", .MaxAge=0},
        });

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=false}),
                .Now=now,
                .SetCookie=cc
            },
            .Out={
                .SetCookie=cc,
                .RespLog=" [cpol u:xxx-yyy fail+dryRun yandexuid=Drop:1!Deletion, yandexuid=Drop:1!Deletion,]",
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-fail_summ", 2},
                    {"cpol-xxx-yyy-cookie-drop-dryRun_summ", 2},
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 4},
                    {"cpol-xxx-yyy-fail_summ", 1},
                    {"cpol-xxx-yyy-fix-dryRun_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 4),
            },
        }));
    }

    Y_UNIT_TEST(TestChange) {
        const auto now = TInstant::Seconds(1596470989);

        const auto cc = ToCookie({
            TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("4000000000000000000")},
            TSetCookie{.Name="other"},
        });

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .SetCookie=cc,
            },
            .Out={
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=4000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=4000000000000000000"},
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));

        // Ok, one of the old values matches
        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=3000000000000000000; yandexuid=4000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3000000000000000000; yandexuid=4000000000000000000"},
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));

        // Ok to replace the old value
        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=0300000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=0300000000000000000"},
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));

        // Ok to replace an old value from the banned list
        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=3337576991517540143"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3337576991517540143"},
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=ToCookie({TSetCookie{.Name="other"}}),
                .RespLog=" [cpol u:xxx-yyy fail+fix yandexuid=Drop:1!ValueChange,]",
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-modified-resp_summ", 1},
                    {"cpol-xxx-yyy-cookie-fail_summ", 1},
                    {"cpol-xxx-yyy-cookie-drop_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-fail_summ", 1},
                    {"cpol-xxx-yyy-fix_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));

        // Allowing change
        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=true, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-pass_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-pass_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));
    }

    Y_UNIT_TEST(TestChangeDryRun) {
        const auto now = TInstant::Seconds(1596470989);

        const auto cc = ToCookie({
            TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("4000000000000000000")},
            TSetCookie{.Name="other"},
        });

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=false, .Fix=false}),
                .Now=now,
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
                .RespLog=" [cpol u:xxx-yyy fail+dryRun yandexuid=Drop:1!ValueChange,]",
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-fail_summ", 1},
                    {"cpol-xxx-yyy-cookie-drop-dryRun_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-fail_summ", 1},
                    {"cpol-xxx-yyy-fix-dryRun_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));
    }

    Y_UNIT_TEST(TestBadValue) {
        const auto now = TInstant::Seconds(1596470989);

        const auto cc = ToCookie({
            TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("xxx")},
            TSetCookie{.Name="other"},
        });

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=true, .Fix=true}),
                .Now=now,
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=ToCookie({TSetCookie{.Name="other"}}),
                .RespLog=" [cpol u:xxx-yyy fail+fix yandexuid=Drop:1!BadValue,]",
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-modified-resp_summ", 1},
                    {"cpol-xxx-yyy-cookie-fail_summ", 1},
                    {"cpol-xxx-yyy-cookie-drop_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-fail_summ", 1},
                    {"cpol-xxx-yyy-fix_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));
    }

    Y_UNIT_TEST(TestBadValueDryRun) {
        const auto now = TInstant::Seconds(1596470989);

        const auto cc = ToCookie({
            TSetCookie{.Name="yandexuid", .Value=NCookie::FromStringBuf("xxx")},
            TSetCookie{.Name="other"},
        });

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeConfig({.AllowChange=true, .Fix=false}),
                .Now=now,
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
            },
            .Out={
                .Cookie={"yandexuid=3000000000000000000"},
                .SetCookie=cc,
                .RespLog=" [cpol u:xxx-yyy fail+dryRun yandexuid=Drop:1!BadValue,]",
                .Stats=FixStatsNoGdpr({
                    {"cpol-xxx-yyy-cookie-fail_summ", 1},
                    {"cpol-xxx-yyy-cookie-drop-dryRun_summ", 1},
                    {"cpol-xxx-yyy-cookie-skip_summ", 1},
                    {"cpol-xxx-yyy-cookie-total_summ", 2},
                    {"cpol-xxx-yyy-fail_summ", 1},
                    {"cpol-xxx-yyy-fix-dryRun_summ", 1},
                    {"cpol-xxx-yyy-total_summ", 1},
                }, 2),
            },
        }));
    }

    Y_UNIT_TEST(TestCreateInterference) {
        const auto now = TInstant::Seconds(1596470989);
        const auto rnd = 44444444444;
        const auto val = GetYuid(rnd, now);

        Y_COOKIE_POLICY_TEST_POLICY((TPolicyTestCase{
            .In={
                .Cfg=MakeCreateConfig(),
                .Now=now,
                .Random=rnd,
                .Host={"yandex.ru"},
                .WithStats=false,
            },
            .Out={
                .Cookie={"yandexuid=" + val},
                .SetCookie=ToCookie({TSetCookie{
                    .Name="yandexuid",
                    .Value=TBlob::NoCopy(val.data(), val.size()),
                    .Path="/",
                    .Domain=".yandex.ru",
                    .Expires=TExpires::FromInstant(now + TDuration::Days(365*10)),
                    .Secure=true,
                }}),
                .ReqLog=" [cpol-req u:xxx-zzz fail+fix yandexuid=Add:1!Create,]",
                .RespLog=" [cpol u:xxx-zzz fail+fix yandexuid=Add:1!Create,]",
            },
        }));
    }

}
