import { describe, it, beforeEach, mock } from "node:test";
import assert from "node:assert/strict";
import { MockFetch } from "../../testHelpers.js";
import "/js/components/plugin-icon.js";

describe("plugin-icon", () => {
  const SAMPLE_SVG =
    '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/></svg>';

  function flush() {
    return new Promise((resolve) => setTimeout(resolve, 0));
  }

  function okResponse(body) {
    return {
      ok: true,
      status: 200,
      statusText: "OK",
      text: async () => body,
    };
  }

  function notFoundResponse() {
    return {
      ok: false,
      status: 404,
      statusText: "Not Found",
      text: async () => "",
    };
  }

  beforeEach(() => {
    document.body.innerHTML = "";
    customElements.get("plugin-icon").cache = new Map();
  });

  describe("PluginIcon - iconset resolution", () => {
    it("resolves an icon from the majesticons set", async () => {
      const fetch = new MockFetch();
      fetch.__intercept("/img/icons/", async () => okResponse(SAMPLE_SVG));
      globalThis.fetch = fetch;

      const element = document.createElement("plugin-icon");
      element.setAttribute("icon", "bell");
      document.body.appendChild(element);
      await flush();

      assert.deepEqual(fetch.calls[0].url, "/img/icons/majesticons/bell.svg");
    });

    it("resolves an icon from the custom set", async () => {
      const fetch = new MockFetch();
      fetch.__intercept("/img/icons/", async () => okResponse(SAMPLE_SVG));
      globalThis.fetch = fetch;

      const element = document.createElement("plugin-icon");
      element.setAttribute("icon", "verified-check");
      document.body.appendChild(element);
      await flush();

      assert.deepEqual(
        fetch.calls[0].url,
        "/img/icons/custom/verified-check.svg",
      );
    });

    it("warns and renders nothing for an unknown icon", async () => {
      const fetch = new MockFetch();
      globalThis.fetch = fetch;

      const originalWarn = console.warn;
      const warnMock = mock.fn();
      console.warn = warnMock;

      try {
        const element = document.createElement("plugin-icon");
        element.setAttribute("icon", "not-a-real-icon");
        document.body.appendChild(element);
        await flush();

        assert.deepEqual(fetch.calls.length, 0);
        assert.deepEqual(element.innerHTML, "");
        assert.deepEqual(warnMock.mock.callCount(), 1);
      } finally {
        console.warn = originalWarn;
      }
    });
  });

  describe("PluginIcon - rendering", () => {
    it("injects the fetched SVG markup", async () => {
      const fetch = new MockFetch();
      fetch.__intercept("/img/icons/", async () => okResponse(SAMPLE_SVG));
      globalThis.fetch = fetch;

      const element = document.createElement("plugin-icon");
      element.setAttribute("icon", "cake");
      document.body.appendChild(element);
      await flush();

      const svg = element.querySelector("svg");
      assert(svg !== null);
      assert.deepEqual(svg.getAttribute("viewBox"), "0 0 24 24");
    });

    it("renders nothing when icon is empty", async () => {
      const fetch = new MockFetch();
      globalThis.fetch = fetch;

      const element = document.createElement("plugin-icon");
      document.body.appendChild(element);
      await flush();

      assert.deepEqual(fetch.calls.length, 0);
      assert.deepEqual(element.innerHTML, "");
    });

    it("swaps the icon when icon changes", async () => {
      const fetch = new MockFetch();
      fetch.__intercept("/img/icons/majesticons/bus.svg", async () =>
        okResponse('<svg id="first"></svg>'),
      );
      fetch.__intercept("/img/icons/majesticons/car.svg", async () =>
        okResponse('<svg id="second"></svg>'),
      );
      globalThis.fetch = fetch;

      const element = document.createElement("plugin-icon");
      element.setAttribute("icon", "bus");
      document.body.appendChild(element);
      await flush();
      assert.deepEqual(element.querySelector("svg").id, "first");

      element.setAttribute("icon", "car");
      await flush();
      assert.deepEqual(element.querySelector("svg").id, "second");
    });
  });

  describe("PluginIcon - caching", () => {
    it("only fetches once when the same icon is rendered twice", async () => {
      const fetch = new MockFetch();
      fetch.__intercept("/img/icons/", async () => okResponse(SAMPLE_SVG));
      globalThis.fetch = fetch;

      const first = document.createElement("plugin-icon");
      first.setAttribute("icon", "chat");
      document.body.appendChild(first);

      const second = document.createElement("plugin-icon");
      second.setAttribute("icon", "chat");
      document.body.appendChild(second);

      await flush();

      assert.deepEqual(fetch.calls.length, 1);
      assert(first.querySelector("svg") !== null);
      assert(second.querySelector("svg") !== null);
    });
  });

  describe("PluginIcon - error handling", () => {
    it("warns and renders nothing when the fetch 404s; does not retry", async () => {
      const fetch = new MockFetch();
      fetch.__intercept("/img/icons/", async () => notFoundResponse());
      globalThis.fetch = fetch;

      const originalWarn = console.warn;
      const warnMock = mock.fn();
      console.warn = warnMock;

      try {
        const element = document.createElement("plugin-icon");
        element.setAttribute("icon", "moon");
        document.body.appendChild(element);
        await flush();

        assert.deepEqual(element.innerHTML, "");
        assert.deepEqual(warnMock.mock.callCount(), 1);

        const retry = document.createElement("plugin-icon");
        retry.setAttribute("icon", "moon");
        document.body.appendChild(retry);
        await flush();

        assert.deepEqual(fetch.calls.length, 1);
      } finally {
        console.warn = originalWarn;
      }
    });
  });
});
