import { getKcContext } from "keycloakify/login/kcContext/getKcContext";
import type { ExtendKcContext } from "keycloakify/login/kcContext/getKcContextFromWindow";
import type { KcContext } from "keycloakify/login/kcContext";
import { same } from "evt/tools/inDepth";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
import { kcContextMocks, kcContextCommonMock } from "keycloakify/login/kcContext/kcContextMocks";
import { deepClone } from "keycloakify/tools/deepClone";
import { expect, it, describe } from "vitest";

describe("getKcContext", () => {
    const authorizedMailDomains = ["example.com", "another-example.com", "*.yet-another-example.com", "*.example.com", "hello-world.com"];

    const displayName = "this is an overwritten common value";

    const aNonStandardValue1 = "a non standard value 1";
    const aNonStandardValue2 = "a non standard value 2";

    type KcContextExtension =
        | {
              pageId: "register.ftl";
              authorizedMailDomains: string[];
          }
        | {
              pageId: "info.ftl";
              aNonStandardValue1: string;
          }
        | {
              pageId: "my-extra-page-1.ftl";
          }
        | {
              pageId: "my-extra-page-2.ftl";
              aNonStandardValue2: string;
          };

    const getKcContextProxy = (params: { mockPageId: ExtendKcContext<KcContextExtension>["pageId"] }) => {
        const { mockPageId } = params;

        const { kcContext } = getKcContext<KcContextExtension>({
            mockPageId,
            "mockData": [
                {
                    "pageId": "login.ftl",
                    "realm": { displayName }
                },
                {
                    "pageId": "info.ftl",
                    aNonStandardValue1
                },
                {
                    "pageId": "register.ftl",
                    authorizedMailDomains
                },
                {
                    "pageId": "my-extra-page-2.ftl",
                    aNonStandardValue2
                }
            ]
        });

        return { kcContext };
    };
    it("has proper API for login.ftl", () => {
        const pageId = "login.ftl";

        const { kcContext } = getKcContextProxy({ "mockPageId": pageId });

        assert(kcContext?.pageId === pageId);

        assert<Equals<typeof kcContext, KcContext.Login>>();

        expect(
            same(
                //NOTE: deepClone for printIfExists or other functions...
                deepClone(kcContext),
                (() => {
                    const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);

                    mock.realm.displayName = displayName;

                    return mock;
                })()
            )
        ).toBe(true);
    });

    it("has a proper API for info.ftl", () => {
        const pageId = "info.ftl";

        const { kcContext } = getKcContextProxy({ "mockPageId": pageId });

        assert(kcContext?.pageId === pageId);

        //NOTE: I don't understand the need to add: pageId: typeof pageId; ...
        assert<
            Equals<
                typeof kcContext,
                KcContext.Info & {
                    pageId: typeof pageId;
                    aNonStandardValue1: string;
                }
            >
        >();

        expect(
            same(
                deepClone(kcContext),
                (() => {
                    const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);

                    Object.assign(mock, { aNonStandardValue1 });

                    return mock;
                })()
            )
        ).toBe(true);
    });
    it("has a proper API for register.ftl", () => {
        const pageId = "register.ftl";

        const { kcContext } = getKcContextProxy({ "mockPageId": pageId });

        assert(kcContext?.pageId === pageId);

        //NOTE: I don't understand the need to add: pageId: typeof pageId; ...
        assert<
            Equals<
                typeof kcContext,
                KcContext.Register & {
                    pageId: typeof pageId;
                    authorizedMailDomains: string[];
                }
            >
        >();

        expect(
            same(
                deepClone(kcContext),
                (() => {
                    const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);

                    Object.assign(mock, { authorizedMailDomains });

                    return mock;
                })()
            )
        ).toBe(true);
    });
    it("has a proper API for my-extra-page-2.ftl", () => {
        const pageId = "my-extra-page-2.ftl";

        const { kcContext } = getKcContextProxy({ "mockPageId": pageId });

        assert(kcContext?.pageId === pageId);

        assert<
            Equals<
                typeof kcContext,
                KcContext.Common & {
                    pageId: typeof pageId;
                    aNonStandardValue2: string;
                }
            >
        >();

        kcContext.aNonStandardValue2;

        expect(
            same(
                deepClone(kcContext),
                (() => {
                    const mock = deepClone(kcContextCommonMock);

                    Object.assign(mock, { pageId, aNonStandardValue2 });

                    return mock;
                })()
            )
        ).toBe(true);
    });
    it("has a proper API for my-extra-page-1.ftl", () => {
        const pageId = "my-extra-page-1.ftl";

        console.log("We expect a warning here =>");

        const { kcContext } = getKcContextProxy({ "mockPageId": pageId });

        assert(kcContext?.pageId === pageId);

        assert<Equals<typeof kcContext, KcContext.Common & { pageId: typeof pageId }>>();

        expect(
            same(
                deepClone(kcContext),
                (() => {
                    const mock = deepClone(kcContextCommonMock);

                    Object.assign(mock, { pageId });

                    return mock;
                })()
            )
        ).toBe(true);
    });
    it("returns the proper mock for login.ftl", () => {
        const pageId = "login.ftl";

        const { kcContext } = getKcContext({
            "mockPageId": pageId
        });

        assert<Equals<typeof kcContext, KcContext | undefined>>();

        assert(same(deepClone(kcContext), deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!)));
    });
    it("returns undefined when no mock is specified", () => {
        const { kcContext } = getKcContext();

        assert<Equals<typeof kcContext, KcContext | undefined>>();

        assert(kcContext === undefined);
    });
});
