import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { createPinia, setActivePinia } from "pinia";
import { beforeEach, describe, expect, it, vi } from "vitest";

import { useActivityStore } from "@/stores/activityStore";

// mock Galaxy object
vi.mock("./activitySetup", () => ({
    defaultActivities: [
        {
            anonymous: false,
            description: "a-description",
            icon: "a-icon" as unknown as IconDefinition,
            id: "a-id",
            mutable: false,
            optional: false,
            panel: true,
            title: "a-title",
            to: null,
            tooltip: "a-tooltip",
            visible: true,
        },
    ],
}));

const newActivities = [
    {
        anonymous: false,
        description: "a-description-new",
        icon: "a-icon-new" as unknown as IconDefinition,
        id: "a-id",
        mutable: false,
        optional: false,
        panel: true,
        title: "a-title",
        to: "a-to-new",
        tooltip: "a-tooltip-new",
        visible: false,
    },
    {
        anonymous: false,
        description: "b-description-new",
        icon: "b-icon-new" as unknown as IconDefinition,
        id: "b-id",
        mutable: true,
        optional: false,
        panel: true,
        title: "b-title-new",
        to: "b-to-new",
        tooltip: "b-tooltip-new",
        visible: true,
    },
];

describe("Activity Store", () => {
    beforeEach(() => {
        setActivePinia(createPinia());
        // ensure clean localStorage between tests (useUserLocalStorage persistence)
        localStorage.clear();
    });

    it("initializes with default activities after sync", async () => {
        const activityStore = useActivityStore("default");
        expect(activityStore.getAll().length).toBe(0);
        await activityStore.sync();
        expect(activityStore.getAll().length).toBe(1);
    });

    it("merges built-in and custom activities on sync", async () => {
        const activityStore = useActivityStore("default");
        await activityStore.sync();
        const initialActivities = activityStore.getAll();
        expect(initialActivities[0]?.visible).toBeTruthy();
        activityStore.setAll(newActivities);
        expect(activityStore.activities.length).toBe(2);
        const currentActivities = activityStore.getAll();
        expect(currentActivities[0]).toEqual(newActivities[0]);
        expect(currentActivities[1]).toEqual(newActivities[1]);
        await activityStore.sync();
        const syncActivities = activityStore.getAll();
        expect(syncActivities.length).toEqual(2);
        expect(syncActivities[0]?.description).toEqual("a-description");
        expect(syncActivities[0]?.visible).toBeFalsy();
        expect(syncActivities[1]).toEqual(newActivities[1]);
    });

    it("removes activities and restores built-ins on sync", async () => {
        const activityStore = useActivityStore("default");
        await activityStore.sync();
        const initialActivities = activityStore.getAll();
        expect(initialActivities.length).toEqual(1);
        activityStore.remove("a-id");
        expect(activityStore.getAll().length).toEqual(0);
        await activityStore.sync();
        expect(activityStore.getAll().length).toEqual(1);
        activityStore.setAll(newActivities);
        expect(activityStore.getAll().length).toEqual(2);
        activityStore.remove("b-id");
        await activityStore.sync();
        expect(activityStore.getAll().length).toEqual(1);
    });

    describe("setPosition", () => {
        it("reorders an activity to the specified position", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();
            activityStore.setAll(newActivities);

            const initialActivities = activityStore.getAll();
            expect(initialActivities[0]?.id).toBe("a-id");
            expect(initialActivities[1]?.id).toBe("b-id");

            // initial order: [a-id, b-id]
            activityStore.setPosition("b-id", 0);

            const reorderedActivities = activityStore.getAll();
            expect(reorderedActivities[0]?.id).toBe("b-id");
            expect(reorderedActivities[1]?.id).toBe("a-id");
        });

        it("bounds the position within valid range", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();
            activityStore.setAll(newActivities);

            // move to an out-of-range index
            activityStore.setPosition("a-id", 100);

            const activities = activityStore.getAll();
            expect(activities[activities.length - 1]?.id).toBe("a-id");
        });

        it("does nothing when activity does not exist", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();
            activityStore.setAll(newActivities);

            const before = activityStore.getAll().map((a) => a.id);
            activityStore.setPosition("non-existent", 0);
            const after = activityStore.getAll().map((a) => a.id);

            expect(after).toEqual(before);
        });
    });

    describe("ensureSideBarOpen", () => {
        it("opens the sidebar for a panel activity", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();
            activityStore.setAll(newActivities);

            // Initially a-id is in the sidebar
            expect(activityStore.toggledSideBar).toBe("a-id");

            // Ensure b-id is open, which should set toggledSideBar to b-id
            activityStore.ensureSideBarOpen("b-id");
            expect(activityStore.toggledSideBar).toBe("b-id");
        });

        it("does nothing for unknown activity", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();

            const previous = activityStore.toggledSideBar;
            activityStore.ensureSideBarOpen("non-existent");
            expect(activityStore.toggledSideBar).toBe(previous);
        });
    });

    describe("setSpecialPanelActivityIds", () => {
        it("prevents sync from resetting toggledSideBar when set to a registered special panel activity", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();

            activityStore.setSpecialPanelActivityIds(["special-panel-id"]);
            activityStore.toggledSideBar = "special-panel-id";

            await activityStore.sync();

            expect(activityStore.toggledSideBar).toBe("special-panel-id");
        });

        it("still resets toggledSideBar when it is not in defaults or registered special activities", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();

            activityStore.setSpecialPanelActivityIds([]);
            activityStore.toggledSideBar = "unknown-panel-id";

            await activityStore.sync();

            expect(activityStore.toggledSideBar).toBe("a-id");
        });

        it("resets toggledSideBar after special activity is unregistered", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();

            activityStore.setSpecialPanelActivityIds(["special-panel-id"]);
            activityStore.toggledSideBar = "special-panel-id";
            await activityStore.sync();
            expect(activityStore.toggledSideBar).toBe("special-panel-id");

            activityStore.setSpecialPanelActivityIds([]);
            await activityStore.sync();
            expect(activityStore.toggledSideBar).toBe("a-id");
        });
    });

    describe("ensureVisible", () => {
        it("marks an existing activity as visible", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();
            activityStore.setAll(newActivities);

            const activity = activityStore.findById("a-id");
            expect(activity?.visible).toBe(false);

            activityStore.ensureVisible("a-id");

            expect(activityStore.findById("a-id")?.visible).toBe(true);
        });

        it("does nothing for unknown activity", async () => {
            const activityStore = useActivityStore("default");
            await activityStore.sync();

            expect(() => activityStore.ensureVisible("non-existent")).not.toThrow();
        });
    });
});
