import { createWrapper, axios } from 'app/test/vitest/helper';
import CrudModel from 'components/CrudModel.vue';
import { vi, afterEach, beforeEach, beforeAll, describe, expect, it } from 'vitest';

describe('CrudModel', () => {
    let wrapper;
    let vm;
    let data;
    beforeAll(() => {
        wrapper = createWrapper(CrudModel, {
            global: {
                stubs: [
                    'vnPaginate',
                    'useState',
                    'arrayData',
                    'useStateStore',
                    'vue-i18n',
                ],
                mocks: {
                    validate: vi.fn(),
                },
            },
            propsData: {
                dataRequired: {
                    fk: 1,
                },
                dataKey: 'crudModelKey',
                model: 'crudModel',
                url: 'crudModelUrl',
                saveFn: '',
            },
        });
        wrapper=wrapper.wrapper;
        vm=wrapper.vm;
    });

    beforeEach(() => {
        vm.fetch([]);
        vm.watchChanges = null;
    });

    afterEach(() => {
        vi.clearAllMocks();
    });

    describe('insert()', () => {
        it('should new element in list with index 0 if formData not has data', () => {
            vm.insert();

            expect(vm.formData.length).toEqual(1);
            expect(vm.formData[0].fk).toEqual(1);
            expect(vm.formData[0].$index).toEqual(0);
        });
    });

    describe('getChanges()', () => {
        it('should return correct updates and creates', async () => {
            vm.fetch([
                { id: 1, name: 'New name one' },
                { id: 2, name: 'New name two' },
                { id: 3, name: 'Bruce Wayne' },
            ]);

            vm.originalData = [
                { id: 1, name: 'Tony Starks' },
                { id: 2, name: 'Jessica Jones' },
                { id: 3, name: 'Bruce Wayne' },
            ];

            vm.insert();
            const result = vm.getChanges();

            const expected = {
                creates: [
                    {
                        $index: 3,
                        fk: 1,
                    },
                ],
                updates: [
                    {
                        data: {
                            name: 'New name one',
                        },
                        where: {
                            id: 1,
                        },
                    },
                    {
                        data: {
                            name: 'New name two',
                        },
                        where: {
                            id: 2,
                        },
                    },
                ],
            };

            expect(result).toEqual(expected);
        });
    });

    describe('getDifferences()', () => {
        it('should return the differences between two objects', async () => {
            const obj1 = {
                a: 1,
                b: 2,
                c: 3,
            };
            const obj2 = {
                a: null,
                b: 4,
                d: 5,
            };

            const result = vm.getDifferences(obj1, obj2);

            expect(result).toEqual({
                a: null,
                b: 4,
                d: 5,
            });
        });
    });

    describe('isEmpty()', () => {
        let dummyObj;
        let dummyArray;
        let result;
        it('should return true if object si null', async () => {
            dummyObj = null;
            result = vm.isEmpty(dummyObj);

            expect(result).toBe(true);
        });

        it('should return true if object si undefined', async () => {
            dummyObj = undefined;
            result = vm.isEmpty(dummyObj);

            expect(result).toBe(true);
        });

        it('should return true if object is empty', async () => {
            dummyObj ={};
            result = vm.isEmpty(dummyObj);    

            expect(result).toBe(true);
        });

        it('should return false if object is not empty', async () => {
            dummyObj = {a:1, b:2, c:3};
            result = vm.isEmpty(dummyObj);

            expect(result).toBe(false);
        });

        it('should return true if array is empty', async () => {
            dummyArray = [];
            result = vm.isEmpty(dummyArray); 

            expect(result).toBe(true);
        });
        
        it('should return false if array is not empty', async () => {
            dummyArray = [1,2,3];
            result = vm.isEmpty(dummyArray);

            expect(result).toBe(false);
        })
    });

    describe('resetData()', () => {
        it('should add $index to elements in data[] and sets originalData and formData with data', async () => {
            data = [{
                name: 'Tony',
                lastName: 'Stark',
                age: 42,
            }];

            vm.resetData(data);
            
            expect(vm.originalData).toEqual(data);
            expect(vm.originalData[0].$index).toEqual(0);
            expect(vm.formData).toEqual(data);
            expect(vm.formData[0].$index).toEqual(0);
            expect(vm.watchChanges).not.toBeNull();
        });

        it('should dont do nothing if data is null', async () => {
            vm.resetData(null);

            expect(vm.watchChanges).toBeNull();
        });

        it('should set originalData and formatData with data and generate watchChanges', async () => {
            data = {
                name: 'Tony',
                lastName: 'Stark',
                age: 42,
            };
            
            vm.resetData(data);

            expect(vm.originalData).toEqual(data);
            expect(vm.formData).toEqual(data);
            expect(vm.watchChanges).not.toBeNull();
        });
    });

    describe('saveChanges()', () => {
        data = [{
            name: 'Tony',
            lastName: 'Stark',
            age: 42,
        }];

        it('should call saveFn if exists', async () => {
            await wrapper.setProps({ saveFn: vi.fn() });

            vm.saveChanges(data);
            
            expect(vm.saveFn).toHaveBeenCalledOnce();
            expect(vm.isLoading).toBe(false);
            expect(vm.hasChanges).toBe(false);

            await wrapper.setProps({ saveFn: '' });
        });

        it("should use default url if there's not saveFn", async () => {
            const postMock =vi.spyOn(axios, 'post');
            
            vm.formData = [{
                name: 'Bruce',
                lastName: 'Wayne',
                age: 45,
            }]

            await vm.saveChanges(data);

            expect(postMock).toHaveBeenCalledWith(vm.url + '/crud', data);
            expect(vm.isLoading).toBe(false);
            expect(vm.hasChanges).toBe(false);
            expect(vm.originalData).toEqual(JSON.parse(JSON.stringify(vm.formData)));
        });
    });
});
