import { getLocalVue } from "@tests/vitest/helpers";
import { mount } from "@vue/test-utils";
import flushPromises from "flush-promises";
import { createPinia, defineStore, setActivePinia } from "pinia";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ref } from "vue";
import Multiselect from "vue-multiselect";

import { getDataset } from "./services";

import SelectionField from "./SelectionField.vue";

vi.mock("@/stores/eventStore", () => ({
    useEventStore: () => ({
        getDragItems: vi.fn(() => [{ id: "item1", name: "Item One", history_content_type: "dataset" }]),
    }),
}));

vi.mock("./services", () => ({
    getHistories: vi.fn(() => Promise.resolve([{ id: "1", name: "History One" }])),
    getDataset: vi.fn(() => Promise.resolve([{ id: "ds1", name: "Dataset A" }])),
    getDatasetCollection: vi.fn(),
    getInvocations: vi.fn(),
    getJobs: vi.fn(),
    getWorkflows: vi.fn(),
}));

const localVue = getLocalVue();
let mockedStore;

// Helper to wait for debounce + async resolution
const waitForDebouncedSearch = async () => {
    await new Promise((resolve) => setTimeout(resolve, 350));
    await flushPromises();
};

vi.mock("@/stores/historyStore", () => {
    return {
        useHistoryStore: () => mockedStore,
    };
});

function mountComponent(props = {}) {
    return mount(SelectionField, {
        localVue,
        propsData: {
            objectType: "history_dataset_id",
            ...props,
        },
    });
}

beforeEach(() => {
    setActivePinia(createPinia());
    const useFakeHistoryStore = defineStore("history", {
        state: () => ({
            currentHistoryId: ref("history123"),
        }),
    });
    mockedStore = useFakeHistoryStore();
});

describe("SelectionField.vue", () => {
    it("renders label and multiselect when ready", async () => {
        const wrapper = mountComponent({ objectName: "My Dataset", objectId: "ds1" });
        await waitForDebouncedSearch();
        const label = wrapper.find("label");
        expect(label.exists()).toBe(true);
        expect(label.text()).toContain("Select a History Dataset Id");
        expect(wrapper.findComponent(Multiselect).exists()).toBeTruthy();
    });

    it("renders empty state when no data found", async () => {
        getDataset.mockImplementationOnce(() => Promise.resolve([]));
        const wrapper = mountComponent();
        await waitForDebouncedSearch();
        expect(wrapper.text()).toContain("No datasets found in your current history");
    });

    it("triggers search when input changes", async () => {
        const wrapper = mountComponent();
        await waitForDebouncedSearch();
        const multiselect = wrapper.findComponent(Multiselect);
        expect(multiselect.exists()).toBe(true);
        multiselect.vm.$emit("search-change", "abc");
        await waitForDebouncedSearch();
        expect(getDataset).toHaveBeenCalledWith("abc", "history123");
    });

    it("shows error if service throws", async () => {
        getDataset.mockImplementationOnce(() => {
            throw new Error("Oops!");
        });
        const wrapper = mountComponent();
        await waitForDebouncedSearch();
        expect(wrapper.text()).toContain("Oops!");
    });

    it("handles dragenter and drop", async () => {
        const wrapper = mountComponent();
        await waitForDebouncedSearch();
        expect(wrapper.classes()).not.toContain("ui-dragover-success");
        await wrapper.trigger("dragenter");
        expect(wrapper.classes()).toContain("ui-dragover-success");
        await wrapper.trigger("drop");
        expect(wrapper.classes()).not.toContain("ui-dragover-success");
        expect(wrapper.emitted("change")).toEqual([[{ id: "item1", name: "Item One" }]]);
    });

    it("uses custom objectQuery function when provided", async () => {
        const mockQuery = vi.fn(() => Promise.resolve([{ id: "custom1", name: "Custom Result" }]));
        const wrapper = mountComponent({ objectQuery: mockQuery });
        await waitForDebouncedSearch();
        const multiselect = wrapper.findComponent(Multiselect);
        multiselect.vm.$emit("search-change", "test");
        await waitForDebouncedSearch();
        expect(mockQuery).toHaveBeenCalledWith("test");
    });

    it("falls back to first option if no objectId and objectName provided", async () => {
        const wrapper = mountComponent({ objectId: "", objectName: "" });
        await waitForDebouncedSearch();
        const multiselect = wrapper.findComponent(Multiselect);
        expect(multiselect.exists()).toBe(true);
        const multiselectOptions = multiselect.props("options");
        expect(multiselectOptions.length).toBeGreaterThan(0);
        await wrapper.vm.$nextTick();
        const currentValue = wrapper.vm.currentValue;
        expect(currentValue.id).toBe("ds1");
        expect(currentValue.name).toBe("Dataset A");
    });
});
