import { test } from "qunit";
import I18n from "I18n";
import { click, settled, visit } from "@ember/test-helpers";
import {
  acceptance,
  exists,
  query,
  queryAll,
  updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { withPluginApi } from "discourse/lib/plugin-api";
import Site from "discourse/models/site";
import { resetCustomCountables } from "discourse/lib/sidebar/user/categories-section/category-section-link";
import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sidebar";
import { bind } from "discourse-common/utils/decorators";

acceptance("Sidebar - Plugin API", function (needs) {
  needs.user();

  needs.settings({
    navigation_menu: "sidebar",
  });

  needs.hooks.afterEach(() => {
    linkDidInsert = undefined;
    linkDestroy = undefined;
    sectionDestroy = undefined;
  });

  let linkDidInsert, linkDestroy, sectionDestroy;

  test("Multiple header actions and links", async function (assert) {
    withPluginApi("1.3.0", (api) => {
      api.addSidebarSection(
        (BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
          return class extends BaseCustomSidebarSection {
            get name() {
              return "test-chat-channels";
            }

            get text() {
              return "chat channels text";
            }

            get actionsIcon() {
              return "cog";
            }

            get actions() {
              return [
                {
                  id: "browseChannels",
                  title: "Browse channels",
                  action: () => {},
                },
                {
                  id: "settings",
                  title: "Settings",
                  action: () => {},
                },
              ];
            }

            @bind
            willDestroy() {
              sectionDestroy = "section test";
            }

            get links() {
              return [
                new (class extends BaseCustomSidebarSectionLink {
                  get name() {
                    return "random-channel";
                  }

                  get classNames() {
                    return "my-class-name";
                  }

                  get route() {
                    return "topic";
                  }

                  get models() {
                    return ["some-slug", 1];
                  }

                  get title() {
                    return "random channel title";
                  }

                  get text() {
                    return "random channel text";
                  }

                  get prefixType() {
                    return "icon";
                  }

                  get prefixValue() {
                    return "hashtag";
                  }

                  get prefixColor() {
                    return "FF0000";
                  }

                  get prefixBadge() {
                    return "lock";
                  }

                  get suffixType() {
                    return "icon";
                  }

                  get suffixValue() {
                    return "circle";
                  }

                  get suffixCSSClass() {
                    return "unread";
                  }

                  @bind
                  didInsert() {
                    linkDidInsert = "link test";
                  }

                  @bind
                  willDestroy() {
                    linkDestroy = "link test";
                  }
                })(),
                new (class extends BaseCustomSidebarSectionLink {
                  get name() {
                    return "dev-channel";
                  }

                  get route() {
                    return "discovery.latest";
                  }

                  get title() {
                    return "dev channel title";
                  }

                  get text() {
                    return "dev channel text";
                  }

                  get prefixColor() {
                    return "alert";
                  }

                  get prefixType() {
                    return "text";
                  }

                  get prefixValue() {
                    return "test text";
                  }
                })(),
                new (class extends BaseCustomSidebarSectionLink {
                  get name() {
                    return "fun-channel";
                  }

                  get route() {
                    return "discovery.latest";
                  }

                  get title() {
                    return "fun channel title";
                  }

                  get text() {
                    return "fun channel text";
                  }

                  get prefixType() {
                    return "image";
                  }

                  get prefixValue() {
                    return "/test.png";
                  }

                  get hoverType() {
                    return "icon";
                  }

                  get hoverValue() {
                    return "times";
                  }

                  get hoverAction() {
                    return () => {};
                  }

                  get hoverTitle() {
                    return "hover button title attribute";
                  }
                })(),
              ];
            }
          };
        }
      );
    });

    await visit("/");

    assert.strictEqual(
      linkDidInsert,
      "link test",
      "calls link didInsert function"
    );

    assert.strictEqual(
      query(
        ".sidebar-section-test-chat-channels .sidebar-section-header-text"
      ).textContent.trim(),
      "chat channels text",
      "displays header with correct text"
    );

    await click(
      ".sidebar-section-test-chat-channels .sidebar-section-header-dropdown summary"
    );

    assert.strictEqual(
      queryAll(
        ".sidebar-section-test-chat-channels .sidebar-section-header-dropdown .select-kit-collection li"
      ).length,
      2,
      "displays two actions"
    );

    const actions = queryAll(
      ".sidebar-section-test-chat-channels .sidebar-section-header-dropdown .select-kit-collection li"
    );

    assert.strictEqual(
      actions[0].textContent.trim(),
      "Browse channels",
      "displays first header action with correct text"
    );

    assert.strictEqual(
      actions[1].textContent.trim(),
      "Settings",
      "displays second header action with correct text"
    );

    const links = queryAll(
      ".sidebar-section-test-chat-channels .sidebar-section-link"
    );

    assert.strictEqual(
      links[0].textContent.trim(),
      "random channel text",
      "displays first link with correct text"
    );

    assert.ok(
      exists(".sidebar-section-link.my-class-name"),
      "sets the custom class name for the section link"
    );

    assert.strictEqual(
      links[0].title,
      "random channel title",
      "displays first link with correct title attribute"
    );

    assert.ok(
      links[0].href.endsWith("/some-slug/1"),
      "link has the correct href attribute"
    );

    assert.strictEqual(
      links[0].children[0].style.color,
      "rgb(255, 0, 0)",
      "has correct prefix color"
    );

    assert.strictEqual(
      links[0].children[0].children[0].classList.contains("d-icon-hashtag"),
      true,
      "displays prefix icon"
    );

    assert.strictEqual(
      links[0].children[0].children[1].classList.contains("d-icon-lock"),
      true,
      "displays prefix icon badge"
    );

    assert.strictEqual(
      links[0].children[2].children[0].classList.contains("d-icon-circle"),
      true,
      "displays suffix icon"
    );

    assert.strictEqual(
      links[1].children[1].textContent.trim(),
      "dev channel text",
      "displays second link with correct text"
    );

    assert.strictEqual(
      links[1].title,
      "dev channel title",
      "displays second link with correct title attribute"
    );

    assert.strictEqual(
      links[1].children[0].style.color,
      "",
      "has no color style when value is invalid"
    );

    assert.strictEqual(
      links[1].children[0].textContent.trim(),
      "test text",
      "displays prefix text"
    );

    assert.strictEqual(
      links[2].children[1].textContent.trim(),
      "fun channel text",
      "displays third link with correct text"
    );

    assert.strictEqual(
      links[2].title,
      "fun channel title",
      "displays third link with correct title attribute"
    );

    assert.strictEqual(
      links[2].children[0].children[0].getAttribute("src"),
      "/test.png",
      "uses correct prefix image url"
    );

    assert.strictEqual(
      query(".sidebar-section-link-hover button").title,
      "hover button title attribute",
      "displays hover button with correct title"
    );

    await click(".btn-sidebar-toggle");

    assert.strictEqual(
      linkDestroy,
      "link test",
      "calls link willDestroy function"
    );

    assert.strictEqual(
      sectionDestroy,
      "section test",
      "calls section willDestroy function"
    );
  });

  test("Single header action and no links", async function (assert) {
    withPluginApi("1.3.0", (api) => {
      api.addSidebarSection((BaseCustomSidebarSection) => {
        return class extends BaseCustomSidebarSection {
          get name() {
            return "test-chat-channels";
          }

          get text() {
            return "chat channels text";
          }

          get actionsIcon() {
            return "cog";
          }

          get actions() {
            return [
              {
                id: "browseChannels",
                title: "Browse channels",
                action: () => {},
              },
            ];
          }

          get links() {
            return [];
          }
        };
      });
    });

    await visit("/");

    assert.strictEqual(
      query(
        ".sidebar-section-test-chat-channels .sidebar-section-header-text"
      ).textContent.trim(),
      "chat channels text",
      "displays header with correct text"
    );

    assert.ok(
      exists("button.sidebar-section-header-button"),
      "displays single header action button"
    );

    assert.ok(
      !exists(".sidebar-section-test-chat-channels .sidebar-section-content a"),
      "displays no links"
    );
  });

  test("API bridge for decorating hamburger-menu widget with footer links", async function (assert) {
    withPluginApi("1.3.0", (api) => {
      api.decorateWidget("hamburger-menu:footerLinks", () => {
        return {
          route: "discovery.top",
          rawLabel: "my top",
          className: "my-custom-top",
        };
      });
    });

    await visit("/");

    await click(
      ".sidebar-section-community .sidebar-more-section-links-details-summary"
    );

    const myCustomTopSectionLink = query(
      ".sidebar-section-community .sidebar-more-section-links-details-content-secondary .sidebar-section-link-my-custom-top"
    );

    assert.ok(
      myCustomTopSectionLink,
      "adds my custom top section link to community section under the secondary section in the More... links drawer"
    );

    assert.ok(
      myCustomTopSectionLink.href.endsWith("/top"),
      "sets the right href attribute for the my custom top section link"
    );

    assert.strictEqual(
      myCustomTopSectionLink.textContent.trim(),
      "my top",
      "displays the right text for my custom top section link"
    );
  });

  test("API bridge for decorating hamburger-menu widget with general links", async function (assert) {
    withPluginApi("1.3.0", (api) => {
      api.decorateWidget("hamburger-menu:generalLinks", () => {
        return {
          route: "discovery.latest",
          label: "filters.latest.title",
        };
      });

      api.decorateWidget("hamburger-menu:generalLinks", () => {
        return {
          route: "discovery.unread",
          rawLabel: "my unreads",
        };
      });

      api.decorateWidget("hamburger-menu:generalLinks", () => {
        return {
          route: "discovery.top",
          rawLabel: "my top",
          className: "my-custom-top",
        };
      });

      api.decorateWidget("hamburger-menu:generalLinks", () => {
        return {
          href: "/c/bug?status=open",
          rawLabel: "open bugs",
        };
      });

      api.decorateWidget("hamburger-menu:generalLinks", () => {
        return {
          href: "/t/internationalization-localization/280",
          rawLabel: "my favourite topic",
        };
      });
    });

    await visit("/");

    const customLatestSectionLink = query(
      ".sidebar-section-community .sidebar-section-link-latest"
    );

    assert.ok(
      customLatestSectionLink,
      "adds custom latest section link to community section"
    );

    assert.ok(
      customLatestSectionLink.href.endsWith("/latest"),
      "sets the right href attribute for the custom latest section link"
    );

    assert.strictEqual(
      customLatestSectionLink.textContent.trim(),
      I18n.t("filters.latest.title"),
      "displays the right text for custom latest section link"
    );

    await click(
      ".sidebar-section-community .sidebar-more-section-links-details-summary"
    );

    const customUnreadSectionLink = query(
      ".sidebar-section-community .sidebar-section-link-my-unreads"
    );

    assert.ok(
      customUnreadSectionLink,
      "adds custom unread section link to community section"
    );

    assert.ok(
      customUnreadSectionLink.href.endsWith("/unread"),
      "sets the right href attribute for the custom unread section link"
    );

    assert.strictEqual(
      customUnreadSectionLink.textContent.trim(),
      "my unreads",
      "displays the right text for custom unread section link"
    );

    const customTopSectionLInk = query(
      ".sidebar-section-community .sidebar-section-link-my-custom-top"
    );

    assert.ok(
      customTopSectionLInk,
      "adds custom top section link to community section with right link class"
    );

    const openBugsSectionLink = query(
      ".sidebar-section-community .sidebar-section-link-open-bugs"
    );

    assert.ok(
      openBugsSectionLink,
      "adds custom open bugs section link to community section with right link class"
    );

    assert.ok(
      openBugsSectionLink.href.endsWith("/c/bug?status=open"),
      "sets the right href attribute for the custom open bugs section link"
    );

    // close more links
    await click(
      ".sidebar-section-community .sidebar-more-section-links-details-summary"
    );

    await visit("/t/internationalization-localization/280");

    assert.ok(
      exists(
        ".sidebar-section-community .sidebar-section-link-my-favourite-topic.active"
      ),
      "displays my favourite topic custom section link when current route matches the link's route"
    );

    await visit("/t/short-topic-with-two-posts/54077");

    assert.notOk(
      exists(
        ".sidebar-section-community .sidebar-section-link-my-favourite-topic.active"
      ),
      "does not display my favourite topic custom section link when current route does not match the link's route"
    );
  });

  test("Section that is not displayed via displaySection", async function (assert) {
    withPluginApi("1.3.0", (api) => {
      api.addSidebarSection((BaseCustomSidebarSection) => {
        return class extends BaseCustomSidebarSection {
          get name() {
            return "test-chat-channels";
          }

          get text() {
            return "chat channels text";
          }

          get actionsIcon() {
            return "cog";
          }

          get actions() {
            return [
              {
                id: "browseChannels",
                title: "Browse channels",
                action: () => {},
              },
            ];
          }

          get links() {
            return [];
          }

          get displaySection() {
            return false;
          }
        };
      });
    });

    await visit("/");

    assert.notOk(
      exists(".sidebar-section-test-chat-channels"),
      "does not display the section"
    );
  });

  test("Registering a custom countable for a section link in the user's sidebar categories section", async function (assert) {
    try {
      return await withPluginApi("1.6.0", async (api) => {
        const categories = Site.current().categories;
        const category1 = categories[0];
        const category2 = categories[1];

        updateCurrentUser({
          sidebar_category_ids: [category1.id, category2.id],
        });

        // User has one unread topic
        this.container.lookup("service:topic-tracking-state").loadStates([
          {
            topic_id: 2,
            highest_post_number: 12,
            last_read_post_number: 11,
            created_at: "2020-02-09T09:40:02.672Z",
            category_id: category1.id,
            notification_level: 2,
            created_in_new_period: false,
            treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
          },
        ]);

        api.registerUserCategorySectionLinkCountable({
          badgeTextFunction: (count) => {
            return `some custom ${count}`;
          },
          route: "discovery.latestCategory",
          routeQuery: { status: "open" },
          shouldRegister: ({ category }) => {
            if (category.name === category1.name) {
              return true;
            } else if (category.name === category2.name) {
              return false;
            }
          },
          refreshCountFunction: ({ category }) => {
            return category.topic_count;
          },
          prioritizeOverDefaults: ({ category }) => {
            return category.topic_count > 1000;
          },
        });

        await visit("/");

        assert.ok(
          exists(
            `.sidebar-section-link-${category1.name} .sidebar-section-link-suffix.unread`
          ),
          "the right suffix is displayed when custom countable is active"
        );

        assert.strictEqual(
          query(`.sidebar-section-link-${category1.name}`).pathname,
          `/c/${category1.name}/${category1.id}`,
          "does not use route configured for custom countable when user has elected not to show any counts in sidebar"
        );

        assert.notOk(
          exists(
            `.sidebar-section-link-${category2.name} .sidebar-section-link-suffix.unread`
          ),
          "does not display suffix when custom countable is not registered"
        );

        updateCurrentUser({
          sidebar_list_destination: UNREAD_LIST_DESTINATION,
        });

        assert.strictEqual(
          query(
            `.sidebar-section-link-${category1.name} .sidebar-section-link-content-badge`
          ).innerText.trim(),
          I18n.t("sidebar.unread_count", { count: 1 }),
          "displays the right badge text in section link when unread is present and custom countable is not prioritised over unread"
        );

        category1.set("topic_count", 2000);

        api.refreshUserSidebarCategoriesSectionCounts();

        await settled();

        assert.strictEqual(
          query(
            `.sidebar-section-link-${category1.name} .sidebar-section-link-content-badge`
          ).innerText.trim(),
          `some custom ${category1.topic_count}`,
          "displays the right badge text in section link when unread is present but custom countable is prioritised over unread"
        );

        assert.strictEqual(
          query(`.sidebar-section-link-${category1.name}`).pathname,
          `/c/${category1.name}/${category1.id}/l/latest`,
          "has the right pathname for section link"
        );

        assert.strictEqual(
          query(`.sidebar-section-link-${category1.name}`).search,
          "?status=open",
          "has the right query params for section link"
        );
      });
    } finally {
      resetCustomCountables();
    }
  });
});
