import { TestBed } from "@angular/core/testing";
import { RouterHandler } from "./router-handler.service";

import { Routes, RouterModule, Router } from "@angular/router";
import { Component } from "@angular/core";

describe("RouterHandler", () => {
  let routerHandlerService: RouterHandler;

  @Component({})
  class Comp {}

  const routes: Routes = [
    { path: "welcome", component: Comp },
    { path: "test", component: Comp },
    { path: "test2/:param", component: Comp },
    { path: "test2/:param1/:param2", component: Comp },
    { path: "test2/:param1/:param2/test/:param3", component: Comp },
    {
      path: "page",
      component: Comp,
      children: [
        { path: "pagechild1", component: Comp },
        { path: "pagechild2", component: Comp },
      ],
    },
    {
      path: "longparent",
      component: Comp,
      children: [
        {
          path: "firstchild1",
          component: Comp,
          children: [
            { path: "secondchild1", component: Comp },
            { path: "secondchild2/:param/sub", component: Comp },
          ],
        },
        { path: "firstchild2/:param", component: Comp },
      ],
    },
    { path: "", component: Comp },
  ];

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        {
          provide: RouterHandler,
          useClass: RouterHandler,
          deps: [Router],
        },
      ],
      imports: [
        RouterModule.forRoot(routes, { relativeLinkResolution: "legacy" }),
      ],
    });

    routerHandlerService = TestBed.inject(RouterHandler);
  });

  // test all used function (setItem, getItem, clear)
  it("should generate the service", () => {
    expect(routerHandlerService).toBeTruthy();
  });

  it("should make flat config without child", () => {
    let conf = [{ path: "page1" }, { path: "page1/:param" }, { path: "page2" }];
    let arr = ["page1", "page1/:param", "page2"];

    expect(
      JSON.stringify(routerHandlerService["parseRouteConfig"](conf)) ==
        JSON.stringify(arr)
    ).toBeTrue();
  });

  it("should make flat config with 1 level child", () => {
    let conf = [
      { path: "page1" },
      { path: "page2", children: [{ path: "child1" }, { path: "child2" }] },
      { path: "page3", children: [{ path: "child3" }, { path: "child4" }] },
    ];
    let arr = [
      "page1",
      "page2",
      "page2/child1",
      "page2/child2",
      "page3",
      "page3/child3",
      "page3/child4",
    ];

    expect(
      JSON.stringify(routerHandlerService["parseRouteConfig"](conf)) ==
        JSON.stringify(arr)
    ).toBeTrue();
  });

  it("should make flat config with X level child", () => {
    let conf = [
      { path: "page1" },
      { path: "page2", children: [{ path: "child1" }, { path: "child2" }] },
      {
        path: "page3",
        children: [
          { path: "child3" },
          {
            path: "child4",
            children: [
              {
                path: "child5",
                children: [
                  {
                    path: "child6",
                    children: [
                      {
                        path: "child7",
                        children: [
                          { path: "child8" },
                          { path: "child9", children: [{ path: "child10" }] },
                        ],
                      },
                    ],
                  },
                  {
                    path: "child11",
                    children: [
                      { path: "child12" },
                      { path: "child13" },
                      { path: "child14" },
                    ],
                  },
                ],
              },
            ],
          },
        ],
      },
    ];
    let arr = [
      "page1",
      "page2",
      "page2/child1",
      "page2/child2",
      "page3",
      "page3/child3",
      "page3/child4",
      "page3/child4/child5",
      "page3/child4/child5/child6",
      "page3/child4/child5/child6/child7",
      "page3/child4/child5/child6/child7/child8",
      "page3/child4/child5/child6/child7/child9",
      "page3/child4/child5/child6/child7/child9/child10",
      "page3/child4/child5/child11",
      "page3/child4/child5/child11/child12",
      "page3/child4/child5/child11/child13",
      "page3/child4/child5/child11/child14",
    ];

    expect(
      JSON.stringify(routerHandlerService["parseRouteConfig"](conf)) ==
        JSON.stringify(arr)
    ).toBeTrue();
  });

  it("should make correct wildcard", (done) => {
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"]("page") == "page"
    ).toBeTrue();
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"]("") == ""
    ).toBeTrue();
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"]("page/page2") ==
        "page/page2"
    ).toBeTrue();
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"]("page/:param") ==
        "page/?"
    ).toBeTrue();
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"](
        "page/:param/page2"
      ) == "page/?/page2"
    ).toBeTrue();
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"](
        "page/:param/page2/:param2"
      ) == "page/?/page2/?"
    ).toBeTrue();
    expect(
      routerHandlerService["makePathWildcardFromFlatConfig"](
        "page/:param/page2/:param2/:param3/:param4/page3/:param5"
      ) == "page/?/page2/?/?/?/page3/?"
    ).toBeTrue();
    done();
  });

  it("should detect correct params", (done) => {
    let test = [
      {
        url: "page",
        params: [],
      },
      {
        url: "page/:param",
        params: [{ param: "param", pos: 1 }],
      },
      {
        url: "page/:param1/:param2",
        params: [
          { param: "param1", pos: 1 },
          { param: "param2", pos: 2 },
        ],
      },
      {
        url: ":param1/page/:param2",
        params: [
          { param: "param1", pos: 0 },
          { param: "param2", pos: 2 },
        ],
      },
      {
        url: "page/:param1/:param2/page2/:param3/page4/:param4",
        params: [
          { param: "param1", pos: 1 },
          { param: "param2", pos: 2 },
          { param: "param3", pos: 4 },
          { param: "param4", pos: 6 },
        ],
      },
    ];

    test.forEach((t) => {
      expect(
        JSON.stringify(
          routerHandlerService["detectParamsFromFlatConfig"](t.url)
        ) == JSON.stringify(t.params)
      ).toBeTrue();
    });

    done();
  });

  it("should isUrlFit work", (done) => {
    let posTest = [
      { url: "page", wildcard: "page" },
      { url: "page/param", wildcard: "page/?" },
      { url: "page/param/subpage", wildcard: "page/?/subpage" },
      { url: "page/param/subpage/param2", wildcard: "page/?/subpage/?" },
      { url: "page/param/subpage/param2", wildcard: "?/?/?/?" },
      {
        url: "page/param/subpage/param2",
        wildcard: "page/param/subpage/param2",
      },
      { url: "x/y/z", wildcard: "x/y/z" },
      { url: "x/y/z", wildcard: "?/?/?" },
      { url: "x/y/z", wildcard: "x/?/?" },
      { url: "x/y/z", wildcard: "?/?/z" },
      { url: "x/y/z", wildcard: "?/y/z" },
    ];

    let negTest = [
      { url: "page", wildcard: "nonpage" },
      { url: "page", wildcard: "" },
      { url: "page", wildcard: "page/?" },
      { url: "page", wildcard: "page/subpage" },
      { url: "page/param", wildcard: "page" },
      { url: "page/param/param2", wildcard: "page/?/param" },
      { url: "page/param/param2", wildcard: "page/?/?/param" },
    ];

    posTest.forEach((t) => {
      expect(routerHandlerService["isUrlFit"](t.url, t.wildcard)).toBeTrue();
    });

    negTest.forEach((t) => {
      expect(routerHandlerService["isUrlFit"](t.url, t.wildcard)).toBeFalse();
    });

    done();
  });

  it("should isSubscriptionPatternFit work", (done) => {
    let posTest = [
      { url: "page", wildcard: "page" },
      { url: "page/param", wildcard: "page/?" },
      { url: "page/param/subpage", wildcard: "page/?/subpage" },
      { url: "page/param/subpage/param2", wildcard: "page/?/subpage/?" },
      { url: "page/param/subpage/param2", wildcard: "?/?/?/?" },
      {
        url: "page/param/subpage/param2",
        wildcard: "page/param/subpage/param2",
      },
      { url: "x/y/z", wildcard: "x/y/z" },
      { url: "x/y/z", wildcard: "?/?/?" },
      { url: "x/y/z", wildcard: "x/?/?" },
      { url: "x/y/z", wildcard: "?/?/z" },
      { url: "x/y/z", wildcard: "?/y/z" },

      { url: "page", wildcard: "page/?" },
      { url: "page", wildcard: "page/subpage" },
      { url: "page", wildcard: "?/subpage" },
      { url: "page/subpage", wildcard: "page/?" },
      { url: "page/subpage", wildcard: "page/?/?" },
      { url: "page/subpage", wildcard: "page/subpage/?" },
    ];

    let negTest = [
      { url: "page", wildcard: "nonpage" },
      { url: "page", wildcard: "" },
      { url: "page/param", wildcard: "page" },
      { url: "page/param/param2", wildcard: "page/?/param" },
    ];

    posTest.forEach((t) => {
      expect(
        routerHandlerService["isSubscriptionPatternFit"](t.url, t.wildcard)
      ).toBeTrue();
    });

    negTest.forEach((t) => {
      expect(
        routerHandlerService["isSubscriptionPatternFit"](t.url, t.wildcard)
      ).toBeFalse();
    });

    done();
  });

  it("should work parameter filler", (done) => {
    let test = [
      {
        url: "page",
        config: {
          wildcard: "page",
          params: [],
          subscriptions: [],
        },
        shouldBe: {
          url: "page",
          params: {},
        },
      },
      {
        url: "page/1234",
        config: {
          wildcard: "page/?",
          params: [{ param: "userId", pos: 1 }],
          subscriptions: [],
        },
        shouldBe: {
          url: "page/1234",
          params: {
            userId: 1234,
          },
        },
      },
      {
        url: "page/1234/subpage",
        config: {
          wildcard: "page",
          params: [{ param: "userId", pos: 1 }],
          subscriptions: [],
        },
        shouldBe: {
          url: "page/1234/subpage",
          params: {
            userId: 1234,
          },
        },
      },
      {
        url: "search/cat_videos/cute/2",
        config: {
          wildcard: "search/?/?/?",
          params: [
            { param: "query", pos: 1 },
            { param: "filter", pos: 2 },
            { param: "page", pos: 3 },
          ],
          subscriptions: [],
        },
        shouldBe: {
          url: "search/cat_videos/cute/2",
          params: {
            query: "cat_videos",
            filter: "cute",
            page: "2",
          },
        },
      },
      {
        url: "user/1234/profil/email",
        config: {
          wildcard: "user/?/profil/email",
          params: [{ param: "userId", pos: 1 }],
          subscriptions: [],
        },
        shouldBe: {
          url: "user/1234/profil/email",
          params: {
            userId: 1234,
          },
        },
      },
      {
        url: "page",
        config: {
          wildcard: "page",
          params: [],
          subscriptions: [],
        },
        shouldBe: {
          url: "page#subpage",
          params: {},
        },
      },
      {
        url: "search/cat_videos",
        config: {
          wildcard: "search/?",
          params: [{ param: "query", pos: 1 }],
          subscriptions: [],
        },
        shouldBe: {
          url: "page#subpage",
          params: {
            query: "cat_videos",
          },
        },
      },
    ];

    test.forEach((t) => {
      let res = routerHandlerService["getParameterUsingRouterPath"](
        t.url,
        t.config
      );

      expect(
        Object.entries(res).length == Object.entries(t.shouldBe.params).length
      ).toBeTrue();

      for (let key in res) {
        expect(
          key in t.shouldBe.params && t.shouldBe.params[key] == res[key]
        ).toBeTrue();
      }
      for (let key in t.shouldBe.params) {
        expect(key in res && t.shouldBe.params[key] == res[key]).toBeTrue();
      }
    });

    done();
  });

  it("should work fragment parse", (done) => {
    let cases = [
      {
        url: "",
        fragment: {},
      },
      {
        url: "f",
        fragment: {
          f: "",
        },
      },
      {
        url: "f!10",
        fragment: {
          f: 10,
        },
      },
      {
        url: "f!10;g!20",
        fragment: {
          f: 10,
          g: 20,
        },
      },
    ];

    for (let i = 0; i < cases.length; i++) {
      let res = routerHandlerService.parseFragment(cases[i].url);

      for (let key in res) {
        expect(
          key in cases[i].fragment && cases[i].fragment[key] == res[key]
        ).toBeTrue();
      }
      for (let key in cases[i].fragment) {
        expect(key in res && cases[i].fragment[key] == res[key]).toBeTrue();
      }
    }
    done();
  });

  it("should generate the correct wildcards", () => {
    let wildcards = [];
    routerHandlerService["config"].forEach((c) => {
      wildcards.push(c.wildcard);
    });
    expect(
      JSON.stringify(wildcards) ==
        JSON.stringify([
          "welcome",
          "test",
          "test2/?",
          "test2/?/?",
          "test2/?/?/test/?",
          "page",
          "page/pagechild1",
          "page/pagechild2",
          "longparent",
          "longparent/firstchild1",
          "longparent/firstchild1/secondchild1",
          "longparent/firstchild1/secondchild2/?/sub",
          "longparent/firstchild2/?",
          "",
        ])
    ).toBeTrue();
  });

  it("should subscribe work", (done) => {
    let handler = () => {};
    routerHandlerService.subscribe("welcome", handler);

    let found = false;

    routerHandlerService["config"].forEach((config) => {
      if (config.wildcard == "welcome") {
        if (config.subscriptions.indexOf(handler) > -1) {
          found = true;
        }
      }
    });

    expect(found).toBeTrue();
    done();
  });

  it("should unsubscribe work", (done) => {
    let handler = () => {};
    routerHandlerService.subscribe("welcome", handler);
    routerHandlerService.unsubscribe("welcome", handler);

    let found = false;

    routerHandlerService["config"].forEach((config) => {
      if (config.wildcard == "welcome") {
        if (config.subscriptions.indexOf(handler) > -1) {
          found = true;
        }
      }
    });

    expect(found).toBeFalse();
    done();
  });

  it("should processUrl work simple", (done) => {
    let result = false;

    let handler = (response) => {
      result = true;
    };
    routerHandlerService.subscribe("welcome", handler);

    let res = routerHandlerService["processUrl"]("welcome");
    res.routerPath.subscriptions.forEach((s) => {
      s(res.routerResponse);
    });

    expect(result).toBeTrue();
    done();
  });

  it("should processUrl work with params", (done) => {
    let result = "";

    let handler = (response) => {
      result = response.params.param;
    };
    routerHandlerService.subscribe("test2", handler);

    let res = routerHandlerService["processUrl"]("test2/42");
    res.routerPath.subscriptions.forEach((s) => {
      s(res.routerResponse);
    });

    expect(result == "42").toBeTrue();
    done();
  });

  it("split should work", (done) => {
    let test = [
      {
        value: "ASD",
        delimiter: "!",
        result: ["ASD", ""],
      },
      {
        value: "ASD!BC",
        delimiter: "!",
        result: ["ASD", "BC"],
      },
      {
        value: "ASD!BC!FG",
        delimiter: "!",
        result: ["ASD", "BC!FG"],
      },
      {
        value: "ASD!!MASD!CASD",
        delimiter: "!",
        result: ["ASD", "!MASD!CASD"],
      },
      {
        value: "ASD!HEY!!WAKKA",
        delimiter: "!!",
        result: ["ASD!HEY", "WAKKA"],
      },
      {
        value: "ASD!!!WAKKAWAKKA!!!!!!WAK",
        delimiter: "!!!",
        result: ["ASD", "WAKKAWAKKA!!!!!!WAK"],
      },
    ];

    for (let i = 0; i < test.length; i++) {
      let res = routerHandlerService["splitByFirstDelimiter"](
        test[i].value,
        test[i].delimiter
      );
      expect(res.length == test[i].result.length).toBeTrue();

      for (let j = 0; j < res.length; j++) {
        expect(res[j] == test[i].result[j]).toBeTrue();
      }
    }

    done();
  });

  it("should test encode/decode fragment", (done) => {
    let test: { parsed; raw; dontBack? }[] = [
      {
        parsed: { a: "!", b: ";", c: ";!;!;!" },
        raw: "a!!;b!;;;c!;;!;;!;;!",
      },
      {
        parsed: { a: ";;", b: ";;;", c: ";" },
        raw: "a!;;;;;b!;;;;;;;c!;;",
      },
      {
        parsed: { "": "x", a: "á", b: "$", c: "!" },
        raw: "!x;a!á;b!$;c!!",
      },
      {
        parsed: { "": "", "ás$:?%+>#&@ó": "~(=/!+ §?_:;>*$<>#&@{}\\|" },
        raw: "!;ás$:?%+>#&@ó!~(=/!+ §?_:;;>*$<>#&@{}\\|",
      },

      // edge case
      {
        parsed: { "": "" },
        raw: "!",
        dontBack: true,
      },
      {
        parsed: { "": "" },
        raw: "!;!",
        dontBack: true,
      },
      {
        parsed: { a: "" },
        raw: "a!",
        dontBack: true,
      },
      {
        parsed: { "": "a" },
        raw: "!a",
      },
      {
        parsed: { a: "" },
        raw: "a",
        dontBack: true,
      },
      {
        parsed: { a: "" },
        raw: "a;",
        dontBack: true,
      },
      {
        parsed: { a: "" },
        raw: ";a",
        dontBack: true,
      },
      {
        parsed: { a: "", b: "" },
        raw: "a;b",
        dontBack: true,
      },
      {
        parsed: { a: "", b: "x" },
        raw: "a;b!x",
        dontBack: true,
      },
      {
        parsed: { a: "", b: "x" },
        raw: "a;b!x",
        dontBack: true,
      },
      {
        parsed: { a: "x", b: "" },
        raw: "a!x;b",
        dontBack: true,
      },
    ];

    test.forEach((t) => {
      let parsed = routerHandlerService.parseFragment(t.raw);

      expect(
        Object.entries(parsed).length == Object.entries(t.parsed).length
      ).toBeTrue();

      for (let key in parsed) {
        expect(key in t.parsed && parsed[key] == t.parsed[key]).toBeTrue();
      }
      for (let key in t.parsed) {
        expect(key in parsed && t.parsed[key] == parsed[key]).toBeTrue();
      }

      let encoded = routerHandlerService.fragmentToRaw(t.parsed);
      if (!t.dontBack) {
        expect(decodeURI(encoded) == t.raw).toBeTrue();
      }
    });

    [{ "!": "" }, { ";": "" }, { "b!c;a": "" }].forEach((element) => {
      try {
        routerHandlerService.fragmentToRaw(element);
        expect(false).toBeTrue();
      } catch (e) {}
    });

    done();

  });
});
