import { createTestingPinia } from "@pinia/testing";
import { getLocalVue } from "@tests/vitest/helpers";
import { mount } from "@vue/test-utils";
import { PiniaVuePlugin } from "pinia";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { nextTick } from "vue";

import { useServerMock } from "@/api/client/__mocks__";
import { defaultActivities } from "@/stores/activitySetup";
import { useActivityStore } from "@/stores/activityStore";

import mountTarget from "./ActivitySettings.vue";

const localVue = getLocalVue();
localVue.use(PiniaVuePlugin);

const { server, http } = useServerMock();
const activityItemSelector = ".activity-settings-item";

function testActivity(id, newOptions = {}) {
    const defaultOptions = {
        id: `activity-test-${id}`,
        description: "activity-test-description",
        icon: "activity-test-icon",
        mutable: true,
        optional: true,
        title: "activity-test-title",
        to: null,
        tooltip: "activity-test-tooltip",
        visible: true,
    };
    return { ...defaultOptions, ...newOptions };
}

async function testSearch(wrapper, query, result) {
    await wrapper.setProps({ query });
    const filtered = wrapper.findAll(activityItemSelector);
    expect(filtered.length).toBe(result);
}

describe("ActivitySettings", () => {
    let activityStore;
    let wrapper;

    beforeEach(async () => {
        const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false });
        // Mock the response of the API call
        server.use(
            http.get("/api/unprivileged_tools", ({ params, query, response }) => {
                return response("4XX").json({ err_code: 400, err_msg: "permission problem" }, { status: 403 });
            }),
        );
        activityStore = useActivityStore(undefined);
        wrapper = mount(mountTarget, {
            localVue,
            pinia,
            props: {
                query: "",
                activityBarId: undefined,
            },
            stubs: {
                FontAwesomeIcon: { template: "<div></div>" },
            },
        });
        await activityStore.sync();
    });

    it("availability of built-in activities", async () => {
        const items = wrapper.findAll(activityItemSelector);
        const nOptional = defaultActivities.filter((x) => x.optional).length - 1; // 403 for unprivileged_tools excludes user-defined-tools activity
        expect(items.length).toBe(nOptional);
    });

    it("visible and optional activity", async () => {
        activityStore.setAll([testActivity("1")]);
        await nextTick();
        const items = wrapper.findAll(activityItemSelector);
        expect(items.length).toBe(1);
        const checkbox = items.at(0).find("[data-title='Hide in Activity Bar']");
        expect(checkbox.exists()).toBeTruthy();
        const icon = wrapper.find("[icon='activity-test-icon']");
        expect(icon.exists()).toBeTruthy();
        expect(activityStore.getAll()[0].visible).toBeTruthy();
        checkbox.trigger("click");
        await wrapper.vm.$nextTick();
        expect(activityStore.getAll()[0].visible).toBeFalsy();
    });

    it("non-visible but optional activity", async () => {
        activityStore.setAll([
            testActivity("1", {
                optional: true,
                visible: false,
            }),
        ]);
        await wrapper.vm.$nextTick();
        const items = wrapper.findAll(activityItemSelector);
        expect(items.length).toBe(1);
        const checkbox = items.at(0).find("[data-title='Show in Activity Bar']");
        expect(checkbox.exists()).toBeTruthy();
        expect(activityStore.getAll()[0].visible).toBeFalsy();
        checkbox.trigger("click");
        await wrapper.vm.$nextTick();
        const visibleCheckbox = items.at(0).find("[data-title='Hide in Activity Bar']");
        expect(visibleCheckbox.exists()).toBeTruthy();
        expect(activityStore.getAll()[0].visible).toBeTruthy();
    });

    it("removable", async () => {
        activityStore.setAll([testActivity("1")]);
        await wrapper.vm.$nextTick();
        const items = wrapper.findAll(activityItemSelector);
        expect(items.length).toBe(1);
        const trash = items.at(0).find("[data-description='delete activity']");
        expect(trash.exists()).toBeTruthy();
        expect(activityStore.getAll().length).toBe(1);
        trash.trigger("click");
        await wrapper.vm.$nextTick();
        expect(activityStore.getAll().length).toBe(0);
    });

    it("non-removable", async () => {
        activityStore.setAll([
            testActivity("1", {
                mutable: false,
            }),
        ]);
        await wrapper.vm.$nextTick();
        const items = wrapper.findAll(activityItemSelector);
        expect(items.length).toBe(1);
        const trash = items.at(0).find("[data-icon='trash']");
        expect(trash.exists()).toBeFalsy();
    });

    it("filter activities by title and description", async () => {
        activityStore.setAll([
            testActivity("1"),
            testActivity("2", { title: "something else" }),
            testActivity("3", { title: "SOMETHING different" }),
            testActivity("4", { description: "SOMEthing odd" }),
        ]);
        await wrapper.vm.$nextTick();
        const items = wrapper.findAll(activityItemSelector);
        expect(items.length).toBe(4);
        await testSearch(wrapper, "else", 1);
        await testSearch(wrapper, "someTHING", 3);
        await testSearch(wrapper, "odd", 1);
    });
});
