import { mount } from "@vue/test-utils";
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-button-horizontal";
import listButton from "@/ux-components/list-button/list-button";
import listCard from "@/ux-components/list-card/list-card";
import radio from "@/ux-components/radio/radio";

describe("input-button-wrapper-mixin", () => {
    describe("mouse clicks", () => {
        describe("checkbox", () => {
            test("click on both => both are selected", async () => {
                // Arrange
                const { wrapper } = setupBaseInputButtonWrapper({
                    mockData: {
                        isMultiSelect: true,
                    },
                });

                // Act
                const buttonWrappers = wrapper.findAllComponents({
                    name: "baseInputButtonWrapper",
                });
                const inputButtonOne = buttonWrappers.at(0);
                const inputButtonTwo = buttonWrappers.at(1);
                expect(inputButtonOne.vm.isMultiSelect).toBe(true);
                expect(inputButtonTwo.vm.isMultiSelect).toBe(true);
                expect(wrapper.vm.value).toEqual([]);

                await inputButtonOne.find("input").setChecked();
                await inputButtonTwo.find("input").setChecked();

                // Assert
                expect(wrapper.vm.value).toEqual(["value1", "value2"]);
            });

            test("click input 1, 2, 1 => only input 2 selected", async () => {
                // Arrange
                const { wrapper } = setupBaseInputButtonWrapper({
                    mockData: {
                        isMultiSelect: true,
                    },
                });

                // Act
                const buttonWrappers = wrapper.findAllComponents({
                    name: "baseInputButtonWrapper",
                });
                const inputButtonOne = buttonWrappers.at(0);
                const inputButtonTwo = buttonWrappers.at(1);
                expect(inputButtonOne.vm.isMultiSelect).toBe(true);
                expect(inputButtonTwo.vm.isMultiSelect).toBe(true);
                expect(wrapper.vm.value).toEqual([]);

                await inputButtonOne.find("input").setChecked();
                await inputButtonTwo.find("input").setChecked();
                await inputButtonOne.find("input").setChecked(false);

                // Assert
                expect(wrapper.vm.value).toEqual(["value2"]);
            });
        });

        describe("radio", () => {
            test("click on both => last clicked is selected", async () => {
                // Arrange
                const { wrapper } = setupBaseInputButtonWrapper({
                    mockData: {
                        isMultiSelect: false,
                    },
                });

                // Act
                const buttonWrappers = wrapper.findAllComponents({
                    name: "baseInputButtonWrapper",
                });
                const inputButtonOne = buttonWrappers.at(0);
                const inputButtonTwo = buttonWrappers.at(1);
                expect(inputButtonOne.vm.isMultiSelect).toBe(false);
                expect(inputButtonTwo.vm.isMultiSelect).toBe(false);
                expect(wrapper.vm.value).toEqual("");

                await inputButtonOne.find("input").setChecked();
                await inputButtonTwo.find("input").setChecked();

                // Assert
                expect(wrapper.vm.value).toEqual("value2");
            });

            test("click input 1, 2, 1 => input 1 is selected", async () => {
                // Arrange
                const { wrapper } = setupBaseInputButtonWrapper({
                    mockData: {
                        isMultiSelect: false,
                    },
                });

                // Act
                const buttonWrappers = wrapper.findAllComponents({
                    name: "baseInputButtonWrapper",
                });
                const inputButtonOne = buttonWrappers.at(0);
                const inputButtonTwo = buttonWrappers.at(1);
                expect(inputButtonOne.vm.isMultiSelect).toBe(false);
                expect(inputButtonTwo.vm.isMultiSelect).toBe(false);
                expect(wrapper.vm.value).toEqual("");

                await inputButtonOne.find("input").setChecked();
                await inputButtonTwo.find("input").setChecked();
                await inputButtonOne.find("input").setChecked();

                // Assert
                expect(wrapper.vm.value).toEqual("value1");
            });
        });
    });

    describe("initial values", () => {
        describe("checkbox", () => {
            const defaultCheckedCases = [
                [["value2"], false, true],
                [["value1", "value2"], true, true],
                [["value1"], true, false],
                [[], false, false],
            ];
            test.each(defaultCheckedCases)(
                "initial value is %s => correct input buttons are selected",
                async (modelValue, isInputButtonOneChecked, isInputButtonTwoChecked) => {
                    // Arrange
                    const { wrapper } = setupBaseInputButtonWrapper({
                        mockData: {
                            isMultiSelect: true,
                            initialValue: modelValue,
                        },
                    });

                    // Act
                    const buttonWrappers = wrapper.findAllComponents({
                        name: "baseInputButtonWrapper",
                    });
                    const inputs = wrapper.findAll("input");
                    const inputButtonOne = buttonWrappers.at(0);
                    const inputButtonTwo = buttonWrappers.at(1);

                    // Assert
                    expect(inputButtonOne.vm.isMultiSelect).toBe(true);
                    expect(inputButtonTwo.vm.isMultiSelect).toBe(true);
                    expect(inputs[0].element.checked).toBe(isInputButtonOneChecked);
                    expect(inputs[1].element.checked).toBe(isInputButtonTwoChecked);
                    expect(wrapper.vm.value).toEqual(modelValue);
                }
            );
        });

        describe("radio", () => {
            const defaultCheckedCases = [
                ["", false, false],
                ["value1", true, false],
                ["value2", false, true],
                [[], false, false],
            ];
            test.each(defaultCheckedCases)(
                "initial value is %s => correct input buttons are selected",
                async (modelValue, isInputButtonOneChecked, isInputButtonTwoChecked) => {
                    // Arrange
                    const { wrapper } = setupBaseInputButtonWrapper({
                        mockData: {
                            isMultiSelect: false,
                            initialValue: modelValue,
                        },
                    });

                    // Act
                    const buttonWrappers = wrapper.findAllComponents({
                        name: "baseInputButtonWrapper",
                    });
                    const inputs = wrapper.findAll("input");
                    const inputButtonOne = buttonWrappers.at(0);
                    const inputButtonTwo = buttonWrappers.at(1);

                    // Assert
                    expect(inputButtonOne.vm.isMultiSelect).toBe(false);
                    expect(inputButtonTwo.vm.isMultiSelect).toBe(false);
                    expect(inputs[0].element.checked).toBe(isInputButtonOneChecked);
                    expect(inputs[1].element.checked).toBe(isInputButtonTwoChecked);
                    expect(wrapper.vm.value).toEqual(modelValue);
                }
            );
        });
    });

    // shared checks between components that use input-button-wrapper-mixin
    describe("shared checks", () => {
        const inputButtonComponents = [listButtonHorizontal, listButton, listCard, radio];
        test.each(inputButtonComponents.map((x) => [x.name, x]))(
            "%s - should return input type checkbox if isMultiSelect is true",
            async (name, inputButtonComponent) => {
                // Act
                const { wrapper } = setupMocksForComponentsUsingInputButtonWrapperMixin({
                    component: inputButtonComponent,
                    mockData: {
                        propsData: {
                            isMultiSelect: true,
                        },
                    },
                });

                // Assert
                const input = wrapper.find("input");
                expect(input.attributes().type).toEqual("checkbox");
            }
        );

        test.each(inputButtonComponents.map((x) => [x.name, x]))(
            "%s - return input type radio if isMultiSelect is false or not specified",
            async (name, inputButtonComponent) => {
                // Act
                const { wrapper } = setupMocksForComponentsUsingInputButtonWrapperMixin({
                    component: inputButtonComponent,
                    mockData: {
                        propsData: {
                            isMultiSelect: false,
                        },
                    },
                });

                // Assert
                const input = wrapper.find("input");
                expect(input.attributes().type).toEqual("radio");
            }
        );

        const selectedValues = ["something", ["test"]];
        inputButtonComponents.forEach((inputButtonComponent) => {
            test.each(selectedValues)(
                `${inputButtonComponent.name} with selectedValue %s - should emit button value on click`,
                async (selectedValue) => {
                    // Act
                    const { wrapper } = setupMocksForComponentsUsingInputButtonWrapperMixin({
                        component: inputButtonComponent,
                        mockData: {
                            propsData: {
                                isRadioHorizontal: true,
                                buttonLabel: "Windshield",
                                value: "List Card Checkbox",
                                groupID: "radio-demo-1",
                                groupName: "radio 1",
                                buttonImage: "windshield-damage.svg",
                                isRequired: true,
                                isWide: false,
                                modelValue: ["List Card Checkbox"],
                                buttonID: "list-card-id",
                            },
                        },
                    });

                    // Act
                    wrapper.vm.selectedValue = selectedValue;
                    await wrapper.vm.$nextTick();

                    // Assert
                    expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(selectedValue);
                }
            );
        });
    });
});

function setupMocksForComponentsUsingInputButtonWrapperMixin({ mockData, component }) {
    const wrapper = mount(component, {
        ...mockData,
        propsData: {
            ...mockData.propsData,
            groupName: "my-group",
            modelValue: mockData.propsData?.isMultiSelect ? ["5"] : "5",
            value: "4",
        },
        mixins: [inputButtonWrapperMixin],
    });

    return { wrapper };
}

function setupBaseInputButtonWrapper({ mockData = {} }) {
    baseInputButton.methods.pushClickEventToGA = jest.fn();

    const baseInputButtonWrapper = {
        name: "baseInputButtonWrapper",
        components: { baseInputButton },
        template: '<div><baseInputButton v-bind="$props" v-model="selectedValue" /></div>',
        mixins: [inputButtonWrapperMixin],
    };

    let parentComponentTemplate = `<div>`;
    parentComponentTemplate += `<baseInputButtonWrapper v-model="value" groupName="myGroupName" :isMultiSelect="${mockData.isMultiSelect}" value="value1" />`;
    parentComponentTemplate += `<baseInputButtonWrapper v-model="value" groupName="myGroupName" :isMultiSelect="${mockData.isMultiSelect}" value="value2" />`;
    parentComponentTemplate += `</div>`;
    const wrapper = mount({
        data() {
            return {
                value: mockData.initialValue ?? (mockData.isMultiSelect ? [] : ""),
                $route: {
                    query: {
                        fmgPage: "myPage",
                    },
                },
            };
        },
        template: parentComponentTemplate,
        components: { baseInputButtonWrapper },
    });

    return { wrapper };
}
