#include <balancer/modules/cookie_policy/common/parse_render.h>
#include <balancer/modules/cookie_policy/ut_common/ut_common.h>

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

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

#include <util/system/src_location.h>

using namespace NModCookiePolicy;
using namespace NSrvKernel::NTesting;


Y_UNIT_TEST_SUITE(TParseRenderTest) {
    struct TParserCase {
        struct TIn {
            TStrVec SetCookie;
            EPolicyMode Mode = EPolicyMode::Fix;
            TMaybe<EPolicyMode> Override = Nothing();
            TMaybe<EPolicyMode> DynOverride = Nothing();
        } In;
        struct TOut {
            TStrVec SetCookie;
            TStatsVec Stats;
            TString ReqLog;
            TString RespLog;
        } Out;
    };

    TParserCase SetMode(TParserCase c, EPolicyMode m) {
        c.In.Mode = m;
        return c;
    }

    TParserCase SetOverride(TParserCase c, EPolicyMode m) {
        c.In.Override = m;
        return c;
    }

    TParserCase SetDynOverride(TParserCase c, EPolicyMode m) {
        c.In.DynOverride = m;
        return c;
    }

    TCPTestInput TestInput(const TParserCase::TIn& c) {
        TCPTestInput in;
        in.Cfg.set_uuid("xxx");
        in.Cfg.set_parser_mode(c.Mode);
        in.SetCookie = c.SetCookie;
        if (c.Override) {
            TPolicyModeCfg pCfg;
            pCfg.set_mode(c.Override);
            TModeControlsCfg cfg;
            cfg.set_parser_mode(pCfg);
            in.Cfg.set_mode_controls(cfg);
        }
        if (c.DynOverride) {
            in.ModeCtl = TStringBuilder() << "{\"parser_mode\":{\"mode\":\"" << c.DynOverride << "\"}}";
        }
        return in;
    }

    TCPTestResult TestResult(const TParserCase::TOut& c) {
        return {
            .SetCookie=c.SetCookie,
            .ReqLog=c.ReqLog,
            .RespLog=c.RespLog,
            .Stats=c.Stats,
        };
    }

    void DoTestParse(const TParserCase& c, TSourceLocation loc) {
        UNIT_ASSERT_VALUES_EQUAL_C(TestCookiePolicy(TestInput(c.In)), TestResult(c.Out), loc);
    }

    void DoTestParseDryRun(TParserCase c, TSourceLocation loc) {
        DoTestParse(SetMode(c, EPolicyMode::DryRun), loc);
        DoTestParse(SetOverride(c, EPolicyMode::DryRun), loc);
        DoTestParse(SetDynOverride(c, EPolicyMode::DryRun), loc);
    }

#define Y_COOKIE_POLICY_TEST_PARSE(c) DoTestParse(c, __LOCATION__)
#define Y_COOKIE_POLICY_TEST_PARSE_DRY_RUN(c) DoTestParseDryRun(c, __LOCATION__)

    Y_UNIT_TEST(TestSkip) {
        // Turning the parser off turns the whole module off
        Y_COOKIE_POLICY_TEST_PARSE((TParserCase{
            .In={.Mode=EPolicyMode::Off},
            .Out={.Stats={
                {"cpol-xxx-off_summ", 1},
                {"cpol-xxx-total_summ", 1},
            }},
        }));
        Y_COOKIE_POLICY_TEST_PARSE((TParserCase{
            .In={.Override=EPolicyMode::Off},
            .Out={.Stats={
                {"cpol-xxx-off_summ", 1},
                {"cpol-xxx-total_summ", 1},
            }},
        }));
        Y_COOKIE_POLICY_TEST_PARSE((TParserCase{
            .In={.DynOverride=EPolicyMode::Off},
            .Out={.Stats={
                {"cpol-xxx-off_summ", 1},
                {"cpol-xxx-total_summ", 1},
            }},
        }));

        // No set-cookie headers - the parser skips the response
        Y_COOKIE_POLICY_TEST_PARSE((TParserCase{
            .Out={.Stats={
                {"cpol-xxx-checked_summ", 1},
                {"cpol-xxx-gdpr-geo-0_summ", 1},
                {"cpol-xxx-gdpr-mod-0_summ", 1},
                {"cpol-xxx-gdpr-src-None_summ", 1},
                {"cpol-xxx-parser-skip_summ", 1},
                {"cpol-xxx-parser-total_summ", 1},
                {"cpol-xxx-gdpr-safe-geo-0_summ", 1},
                {"cpol-xxx-gdpr-safe-mod-0_summ", 1},
                {"cpol-xxx-gdpr-safe-src-None_summ", 1},
                {"cpol-xxx-total_summ", 1},
            }},
        }));
    }

    Y_UNIT_TEST(TestParse) {
        Y_COOKIE_POLICY_TEST_PARSE((TParserCase{
            .In={.SetCookie={"c;\"d",}},
            .Out={
                .Stats={
                    {"cpol-xxx-checked_summ", 1},
                    {"cpol-xxx-gdpr-geo-0_summ", 1},
                    {"cpol-xxx-gdpr-mod-0_summ", 1},
                    {"cpol-xxx-gdpr-src-None_summ", 1},
                    {"cpol-xxx-modified-resp_summ", 1},
                    {"cpol-xxx-parser-cookie-drop_summ", 1},
                    {"cpol-xxx-parser-cookie-fail_summ", 1},
                    {"cpol-xxx-parser-cookie-total_summ", 1},
                    {"cpol-xxx-parser-err-AttrUnknown_summ", 1},
                    {"cpol-xxx-parser-err-CookieNameEmpty_summ", 1},
                    {"cpol-xxx-parser-fail_summ", 1},
                    {"cpol-xxx-parser-fix_summ", 1},
                    {"cpol-xxx-parser-total_summ", 1},
                    {"cpol-xxx-gdpr-safe-src-None_summ", 1},
                    {"cpol-xxx-gdpr-safe-geo-0_summ", 1},
                    {"cpol-xxx-gdpr-safe-mod-0_summ", 1},
                    {"cpol-xxx-total_summ", 1},
                },
                .RespLog=" [cpol u:xxx-parser fail+fix (c;\\\"d)=Drop:1!CookieNameEmpty,AttrUnknown,]",
            },
        }));

        Y_COOKIE_POLICY_TEST_PARSE((TParserCase{
            .In={
                .SetCookie={
                    "a=b",
                    "c;\"d",
                    "e=f",
                    "g=h; max-age=0; expires=Tue, 02 Oct 2096 10:06:40 GMT"
                }
            },
            .Out={
                .SetCookie={
                    "a=b",
                    "e=f",
                    "g=h; max-age=0; expires=Tue, 02 Oct 2096 10:06:40 GMT"
                },
                .Stats={
                    {"cpol-xxx-checked_summ", 1},
                    {"cpol-xxx-gdpr-geo-0_summ", 1},
                    {"cpol-xxx-gdpr-mod-0_summ", 1},
                    {"cpol-xxx-gdpr-src-None_summ", 1},
                    {"cpol-xxx-modified-resp_summ", 1},
                    {"cpol-xxx-parser-cookie-drop_summ", 1},
                    {"cpol-xxx-parser-cookie-fail_summ", 1},
                    {"cpol-xxx-parser-cookie-pass_summ", 3},
                    {"cpol-xxx-parser-cookie-total_summ", 4},
                    {"cpol-xxx-parser-err-AttrUnknown_summ", 1},
                    {"cpol-xxx-parser-err-CookieNameEmpty_summ", 1},
                    {"cpol-xxx-parser-fail_summ", 1},
                    {"cpol-xxx-parser-fix_summ", 1},
                    {"cpol-xxx-parser-total_summ", 1},
                    {"cpol-xxx-gdpr-safe-geo-0_summ", 1},
                    {"cpol-xxx-gdpr-safe-mod-0_summ", 1},
                    {"cpol-xxx-gdpr-safe-src-None_summ", 1},
                    {"cpol-xxx-total_summ", 1},
                },
                .RespLog=" [cpol u:xxx-parser fail+fix (c;\\\"d)=Drop:1!CookieNameEmpty,AttrUnknown,]",
            },
        }));

        Y_COOKIE_POLICY_TEST_PARSE_DRY_RUN((TParserCase{
            .In={
                .SetCookie={
                    "a=b",
                    "c;\"d",
                    "e=f",
                    "g=h; max-age=0; expires=Tue, 02 Oct 2096 10:06:40 GMT"
                },
            },
            .Out={
                .SetCookie={
                    "a=b",
                    "c;\"d",
                    "e=f",
                    "g=h; max-age=0; expires=Tue, 02 Oct 2096 10:06:40 GMT"
                },
                .Stats={
                    {"cpol-xxx-checked_summ", 1},
                    {"cpol-xxx-gdpr-geo-0_summ", 1},
                    {"cpol-xxx-gdpr-mod-0_summ", 1},
                    {"cpol-xxx-gdpr-src-None_summ", 1},
                    {"cpol-xxx-parser-cookie-drop-dryRun_summ", 1},
                    {"cpol-xxx-parser-cookie-fail_summ", 1},
                    {"cpol-xxx-parser-cookie-pass_summ", 3},
                    {"cpol-xxx-parser-cookie-total_summ", 4},
                    {"cpol-xxx-parser-err-AttrUnknown_summ", 1},
                    {"cpol-xxx-parser-err-CookieNameEmpty_summ", 1},
                    {"cpol-xxx-parser-fail_summ", 1},
                    {"cpol-xxx-parser-fix-dryRun_summ", 1},
                    {"cpol-xxx-parser-total_summ", 1},
                    {"cpol-xxx-gdpr-safe-geo-0_summ", 1},
                    {"cpol-xxx-gdpr-safe-mod-0_summ", 1},
                    {"cpol-xxx-gdpr-safe-src-None_summ", 1},
                    {"cpol-xxx-total_summ", 1},
                },
                .RespLog=" [cpol u:xxx-parser fail+dryRun (c;\\\"d)=Drop:1!CookieNameEmpty,AttrUnknown,]",
            },
        }));
    }
}
