import { shallowMount } from "@vue/test-utils";
import textboxQuestion from "./textbox-question";

// Mock CMS content
const questionText = "Question Text";
const mockMixin = {
    methods: {
        getCmsContent: jest.fn().mockImplementation(() => {
            return questionText;
        }),
    },
};
const maska = jest.fn();

describe("textboxQuestion.vue", () => {
    it("Should render a text input", async () => {
        // Arrange
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            mixins: [mockMixin],
        });

        wrapper.getCmsContent = jest.fn();

        // Act
        const input = wrapper.find("input");

        // Assert
        expect(input.exists()).toBe(true);
    });

    it("Should return rounded-pill class", async () => {
        // Act
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            mixins: [mockMixin],
            propsData: {
                cornerStyle: "rounded",
            },
        });

        // Assert
        const paragraph = wrapper.find("input");

        expect(paragraph.attributes("class")).toContain("rounded-pill");
    });

    it("Should return form-control class", async () => {
        // Act
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            mixins: [mockMixin],
            propsData: {
                questionAlignment: "text",
            },
        });

        // Assert
        const paragraph = wrapper.find("input");

        expect(paragraph.attributes("class")).toContain("form-control");
    });

    it("Should render the 'questionText' data value as the aria-label attribute.", async () => {
        // Arrange
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            mixins: [mockMixin],
        });

        // Act
        const label = wrapper.find("label");

        // Assert
        expect(label.attributes("aria-label")).toContain(questionText);
    });

    it("Should return aria-disabled state as disabled", async () => {
        // Arrange
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            propsData: {
                isDisabled: true,
            },
            mixins: [mockMixin],
        });

        // Assert
        const input = wrapper.find("input");

        // Expect
        expect(input.attributes("aria-disabled")).toEqual("true");
    });

    it("Should emit new value when modelValue is changed", async () => {
        // Act
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            propsData: {
                modelValue: "val",
            },
            mixins: [mockMixin],
        });

        await wrapper.find("input").setValue("val2");

        // Assert
        expect(wrapper.emitted()).toHaveProperty("change");
    });

    it("Should call this.handleChange with new value when the value is changed and the new value is valid", async () => {
        // Arrange
        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            propsData: {
                options: {},
                modelValue: "foo",
            },
            mixins: [mockMixin],
        });

        wrapper.vm.handleChange = jest.fn().mockImplementation(() => {});
        wrapper.vm.validate = jest.fn().mockImplementation(() => {
            return true;
        });

        // Act
        wrapper.vm.$options.watch.value.call(wrapper.vm, "bar");

        // Assert
        expect(wrapper.vm.handleChange).toHaveBeenCalled;
    });

    it("Should not display camera or spinner icon when disabled", () => {
        // Arrange
        const inputId = "test";

        const wrapper = shallowMount(textboxQuestion, {
            global: {
                directives: {
                    maska: maska,
                },
            },
            propsData: {
                modelValue: "",
                includeImageQuestion: true,
                inputId: inputId,
                isDisabled: true,
            },
            mixins: [mockMixin],
            attachTo: document.body,
        });

        // Act
        const cameraIcon = wrapper.find('[data-test="image-upload"]');
        const loadingIcon = wrapper.findComponent("loader");

        // Assert
        expect(cameraIcon.exists()).toBe(false);
        expect(loadingIcon.exists()).toBe(false);
    });

    describe("Image Uploading", () => {
        it("Should call this.handleChange & emit update when fully valid image is submitted.", async () => {
            // Arrange
            const inputId = "test";
            const responseValue = "testResponse";

            const imageHandler = jest.fn().mockImplementation(
                () =>
                    new Promise((resolve, reject) => {
                        resolve({
                            data: [responseValue],
                        });
                    })
            );

            const wrapper = shallowMount(textboxQuestion, {
                global: {
                    directives: {
                        maska: maska,
                    },
                },
                propsData: {
                    modelValue: "",
                    "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }),
                    includeImageQuestion: true,
                    inputId: inputId,
                    imageQuestionSubmitHandler: imageHandler,
                },
                mixins: [mockMixin],
                attachTo: document.body,
            });

            wrapper.vm.handleChange = jest.fn().mockImplementation(() => {});
            wrapper.vm.isImageValid = jest.fn().mockImplementation(() => true);

            // Act
            const imageUploadField = wrapper.find('[data-test="image-upload"]');

            await imageUploadField.trigger("change");

            await wrapper.vm.$nextTick();

            // Assert

            expect(wrapper.vm.imageQuestionSubmitHandler).toHaveBeenCalled();
            expect(wrapper.vm.handleChange).toHaveBeenCalled();
            expect(wrapper.emitted()).toHaveProperty("update:modelValue");
        });

        it("Should not call handler and emit image-lookup-error when image is invalid.", async () => {
            // Arrange
            const inputId = "test";
            const responseValue = "testResponse";

            const imageHandler = jest.fn().mockImplementation(
                () =>
                    new Promise((resolve, reject) => {
                        resolve({
                            data: [responseValue],
                        });
                    })
            );

            const wrapper = shallowMount(textboxQuestion, {
                global: {
                    directives: {
                        maska: maska,
                    },
                },
                propsData: {
                    modelValue: "",
                    "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }),
                    includeImageQuestion: true,
                    inputId: inputId,
                    imageQuestionSubmitHandler: imageHandler,
                },
                mixins: [mockMixin],
                attachTo: document.body,
            });

            wrapper.vm.handleChange = jest.fn().mockImplementation(() => {});
            wrapper.vm.isImageValid = jest.fn().mockImplementation(() => false);

            // Act
            const imageUploadField = wrapper.find('[data-test="image-upload"]');

            await imageUploadField.trigger("change");

            await wrapper.vm.$nextTick();

            // Assert
            expect(wrapper.vm.imageQuestionSubmitHandler).not.toHaveBeenCalled();
            expect(wrapper.vm.handleChange).not.toHaveBeenCalled();
            expect(wrapper.emitted()).toHaveProperty("imageValidityError");
        });

        it("Should emit image-lookup-error when image lookup responds with an error.", async () => {
            // Arrange
            const inputId = "test";

            const imageHandler = jest.fn().mockImplementation(
                () =>
                    new Promise((resolve, reject) => {
                        reject({});
                    })
            );

            const wrapper = shallowMount(textboxQuestion, {
                global: {
                    directives: {
                        maska: maska,
                    },
                },
                propsData: {
                    modelValue: "",
                    "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }),
                    includeImageQuestion: true,
                    inputId: inputId,
                    imageQuestionSubmitHandler: imageHandler,
                },
                mixins: [mockMixin],
                attachTo: document.body,
            });

            wrapper.vm.handleChange = jest.fn().mockImplementation(() => {});
            wrapper.vm.isImageValid = jest.fn().mockImplementation(() => true);

            // Act
            const imageUploadField = wrapper.find('[data-test="image-upload"]');

            await imageUploadField.trigger("change");

            await wrapper.vm.$nextTick();

            // Assert
            expect(wrapper.vm.imageQuestionSubmitHandler).toHaveBeenCalled();
            expect(wrapper.vm.handleChange).not.toHaveBeenCalled();
            expect(wrapper.emitted()).toHaveProperty("imageLookupError");
        });
    });

    describe("Image Validation", () => {
        it("Should return true if a valid image is tested.", () => {
            // Arrange
            const wrapper = shallowMount(textboxQuestion, {
                global: {
                    directives: {
                        maska: maska,
                    },
                },
                propsData: {
                    inputId: "input ID",
                    maxFileSize: 5140 * 1028,
                },
                mixins: [mockMixin],
            });

            const image = {
                size: 100 * 1028,
            };

            // Act
            const result = wrapper.vm.isImageValid(image);

            // Assert
            expect(result).toBeTruthy();
        });

        it("Should return false if null image is tested.", () => {
            // Arrange
            const wrapper = shallowMount(textboxQuestion, {
                global: {
                    directives: {
                        maska: maska,
                    },
                },
                propsData: {
                    inputId: "input ID",
                    maxFileSize: 5140 * 1028,
                },
                mixins: [mockMixin],
            });

            const image = null;

            // Act
            const result = wrapper.vm.isImageValid(image);

            // Assert
            expect(result).toBeFalsy();
        });

        it("Should return false if oversized image is tested.", () => {
            // Arrange
            const wrapper = shallowMount(textboxQuestion, {
                global: {
                    directives: {
                        maska: maska,
                    },
                },
                propsData: {
                    inputId: "input ID",
                    maxFileSize: 5140 * 1028,
                },
                mixins: [mockMixin],
            });

            const image = {
                size: 10000000 * 1028,
            };

            // Act
            const result = wrapper.vm.isImageValid(image);

            // Assert
            expect(result).toBeFalsy();
        });
    });
});
