import { defineComponent, nextTick } from 'vue';
import { usePagination } from './pagination.composable';
import { useRoute } from 'vue-router';
import { mount } from '@vue/test-utils';

vi.mock('vue-router', () => ({
  useRoute: vi.fn(),
}));

let saveUserPrefSpy: any;
let getUserPrefSpy: any;

const factory = (props?: any) => {
  return mount(
    defineComponent({
      props: {
        persistent: Boolean || undefined,
        initialSize: Object as any,
        initialPagination: Object as any,
      },
      setup(props) {
        return usePagination(
          props.persistent,
          props.initialSize || undefined,
          props.initialPagination || undefined,
        );
      },
      template: '<div/>',
    }),
    { props: props },
  );
};

vi.mock('@/composables/userPref.composable', () => ({
  useUserPref: () => ({
    saveUserPref: saveUserPrefSpy,
    getUserPref: getUserPrefSpy,
  }),
}));

describe('pagination composable', () => {
  let previous: any;
  let replaceStateSpy: any;
  beforeEach(() => {
    vi.resetAllMocks();
    (useRoute as any).mockImplementation(() => ({ path: 'path' }));
    saveUserPrefSpy = vi.fn().mockImplementation(() => {});
    getUserPrefSpy = vi.fn().mockReturnValue(undefined);
    replaceStateSpy = vi
      .spyOn(window.history, 'replaceState')
      .mockImplementation(() => {});
    previous = window.location;
    //@ts-ignore
    delete window.location;
    //@ts-ignore
    window.location = { search: null };
  });
  afterEach(() => {
    window.location = previous;
  });
  it('should compute options pagination', async () => {
    const wrapper = factory();
    wrapper.vm.size = 5;
    expect(wrapper.vm.paginationsOptions).toStrictEqual([5, 25, 50, 100]);
  });

  it('should compute options pagination in right order', async () => {
    const wrapper = factory();
    wrapper.vm.size = 5;
    wrapper.vm.pagination = [25, 10, 100, 50];
    expect(wrapper.vm.paginationsOptions).toStrictEqual([5, 10, 25, 50, 100]);
  });

  it('should show right page items', async () => {
    const wrapper = factory();
    wrapper.vm.items = [1, 2, 3, 4, 5, 6];
    wrapper.vm.size = 2;

    expect(wrapper.vm.currentItems).toStrictEqual([1, 2]);

    wrapper.vm.currentPage = 1;
    expect(wrapper.vm.currentItems).toStrictEqual([3, 4]);

    wrapper.vm.currentPage = 5;
    expect(wrapper.vm.currentItems).toStrictEqual([]);
  });

  it('should show right number of pages', async () => {
    const wrapper = factory();
    wrapper.vm.items = [1, 2, 3, 4, 5, 6];
    wrapper.vm.size = 2;

    expect(wrapper.vm.totalPages).toBe(3);
  });

  it('should go to the right page', async () => {
    const wrapper = factory();
    wrapper.vm.items = [1, 2, 3, 4, 5, 6];
    wrapper.vm.size = 2;

    wrapper.vm.goToPage(1);
    expect(wrapper.vm.currentPage).toBe(1);
  });

  it('should not go over max page', async () => {
    const wrapper = factory();
    wrapper.vm.items = [1, 2, 3, 4, 5, 6];
    wrapper.vm.size = 2;

    wrapper.vm.goToPage(5);
    expect(wrapper.vm.currentPage).toBe(2);
  });

  it('should not go under min page', async () => {
    const wrapper = factory();
    wrapper.vm.items = [1, 2, 3, 4, 5, 6];
    wrapper.vm.size = 2;

    wrapper.vm.goToPage(-4);
    expect(wrapper.vm.currentPage).toBe(0);
  });

  it('should save size in userPref if it is changed and persistent', async () => {
    window.location = {
      ...previous,
      protocol: 'https:',
      host: 'localhost/',
      pathname: 'path',
      search: '',
    };
    const wrapper = factory({ persistent: true });
    wrapper.vm.size = 100;
    await nextTick();
    expect(saveUserPrefSpy).toHaveBeenCalled();
    expect(saveUserPrefSpy).toHaveBeenCalledWith(
      'pagination-size',
      100,
      'path',
    );
    expect(replaceStateSpy).toHaveBeenCalledWith(
      { path: 'https://localhost/path?table-size=100' },
      '',
      'https://localhost/path?table-size=100',
    );
  });

  it('should not save size in userPref if it is changed and not persistent', async () => {
    const wrapper = factory({ persistent: false });
    wrapper.vm.size = 100;
    await nextTick();
    expect(saveUserPrefSpy).not.toHaveBeenCalled();
  });

  describe('initPagination', () => {
    describe('size', () => {
      it('should init size value with default if not persistent', async () => {
        getUserPrefSpy = vi.fn().mockReturnValue(undefined);
        const wrapper = factory({ persistent: false });
        await nextTick();
        expect(wrapper.vm.paginationsOptions).toStrictEqual([25, 50, 100]);
        expect(wrapper.vm.size).toEqual(25);
        expect(getUserPrefSpy).not.toHaveBeenCalled();
      });

      it('should init size value with what is in userPref if persistent', async () => {
        getUserPrefSpy = vi.fn().mockReturnValue(5);
        const wrapper = factory({ persistent: true });
        await nextTick();
        expect(wrapper.vm.paginationsOptions).toStrictEqual([5, 25, 50, 100]);
        expect(wrapper.vm.size).toEqual(5);
      });

      it('should init size value with default if not in userPref if persistent', async () => {
        getUserPrefSpy = vi.fn().mockReturnValue(undefined);
        const wrapper = factory();
        await nextTick();
        expect(wrapper.vm.paginationsOptions).toStrictEqual([25, 50, 100]);
        expect(wrapper.vm.size).toEqual(25);
      });

      it('should init size value with value in path if exists', async () => {
        window.location = {
          ...previous,
          protocol: 'https:',
          host: 'localhost/',
          pathname: 'path',
          search: '?table-size=5',
        };
        getUserPrefSpy = vi.fn().mockReturnValue(undefined);
        const wrapper = factory({ persistent: true });
        await nextTick();
        expect(wrapper.vm.paginationsOptions).toStrictEqual([5, 25, 50, 100]);
        expect(wrapper.vm.size).toEqual(5);
      });
    });
    describe('currentPage', () => {
      it('should init currentPage with default if not in path', () => {
        window.location = {
          ...previous,
          protocol: 'https:',
          host: 'localhost/',
          pathname: 'path',
          search: '?',
        };
        const wrapper = factory({ persistent: true });
        expect(wrapper.vm.currentPage).toEqual(0);
      });
      it('should init currentPage with default if in path but not number', () => {
        window.location = {
          ...previous,
          protocol: 'https:',
          host: 'localhost/',
          pathname: 'path',
          search: '?table-page=toto',
        };
        const wrapper = factory({ persistent: true });
        expect(wrapper.vm.currentPage).toEqual(0);
      });
      it('should init currentPage with path value if in path', () => {
        window.location = {
          ...previous,
          protocol: 'https:',
          host: 'localhost/',
          pathname: 'path',
          search: '?table-page=5',
        };
        const wrapper = factory({ persistent: true });
        expect(wrapper.vm.currentPage).toEqual(5);
      });
    });
  });

  describe('currentPage', () => {
    it('should not save to path if not persistent', async () => {
      const wrapper = factory({ persistent: false });
      wrapper.vm.currentPage = 5;
      await nextTick();
      expect(replaceStateSpy).not.toHaveBeenCalled();
    });
    it('should save to path if persistent', async () => {
      window.location = {
        ...previous,
        protocol: 'https:',
        host: 'localhost/',
        pathname: 'path',
        search: '',
      };
      const wrapper = factory({ persistent: true });
      wrapper.vm.currentPage = 5;
      await nextTick();
      expect(replaceStateSpy).toHaveBeenCalledWith(
        { path: 'https://localhost/path?table-page=5' },
        '',
        'https://localhost/path?table-page=5',
      );
    });
  });
});
