import { createPopper } from "@popperjs/core";
import { mount } from "@vue/test-utils";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { nextTick, ref } from "vue";

import { usePopper } from "./usePopper";

vi.mock("@popperjs/core", () => ({
    createPopper: vi.fn(() => ({
        destroy: vi.fn(),
        update: vi.fn(),
    })),
}));

describe("usePopper", () => {
    let referenceElement;
    let popperElement;

    beforeEach(() => {
        referenceElement = document.createElement("div");
        document.body.appendChild(referenceElement);

        popperElement = document.createElement("div");
        document.body.appendChild(popperElement);
    });

    afterEach(() => {
        document.body.innerHTML = "";
        vi.clearAllMocks();
    });

    const createTestComponent = (trigger = "none") => {
        return mount({
            template: "<div></div>",
            setup() {
                const reference = ref(referenceElement);
                const popper = ref(popperElement);
                const options = { placement: "bottom", trigger };
                const { visible, instance } = usePopper(reference, popper, options);
                return { visible, instance };
            },
        });
    };

    test("should initialize Popper instance on mount", () => {
        createTestComponent();
        expect(createPopper).toHaveBeenCalledWith(referenceElement, popperElement, {
            placement: "bottom",
            modifiers: [
                {
                    name: "offset",
                    options: {
                        offset: [0, 5],
                    },
                },
            ],
            strategy: "absolute",
        });
    });

    test("should destroy Popper instance on unmount", () => {
        const wrapper = createTestComponent();
        const popperInstance = createPopper.mock.results[0].value;
        wrapper.destroy();
        expect(popperInstance.destroy).toHaveBeenCalled();
    });

    test("should not change visibility for trigger 'none'", async () => {
        const wrapper = createTestComponent("none");
        const { visible } = wrapper.vm;

        expect(visible).toBe(false);

        referenceElement.dispatchEvent(new Event("click"));
        await nextTick();
        expect(visible).toBe(false);
    });
});
