import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { GetStaticPropsContext } from 'next';
import { ParsedUrlQuery } from 'querystring';
import { RouterContext } from 'next/dist/next-server/lib/router-context';

import { getPrismicClient } from '../../services/prismic';
import App, { getStaticProps } from '../../pages';

interface Post {
  uid?: string;
  first_publication_date: string | null;
  data: {
    title: string;
    subtitle: string;
    author: string;
  };
}

interface PostPagination {
  next_page: string;
  results: Post[];
}

interface HomeProps {
  postsPagination: PostPagination;
}

interface GetStaticPropsResult {
  props: HomeProps;
}

const mockedQueryReturn = {
  next_page: 'link',
  results: [
    {
      uid: 'como-utilizar-hooks',
      first_publication_date: '2021-03-15T19:25:28+0000',
      data: {
        title: 'Como utilizar Hooks',
        subtitle: 'Pensando em sincronização em vez de ciclos de vida',
        author: 'Joseph Oliveira',
      },
    },
    {
      uid: 'criando-um-app-cra-do-zero',
      first_publication_date: '2021-03-25T19:27:35+0000',
      data: {
        title: 'Criando um app CRA do zero',
        subtitle:
          'Tudo sobre como criar a sua primeira aplicação utilizando Create React App',
        author: 'Danilo Vieira',
      },
    },
  ],
};

jest.mock('@prismicio/client');
jest.mock('../../services/prismic');

const mockedPrismic = getPrismicClient as jest.Mock;
const mockedFetch = jest.spyOn(window, 'fetch') as jest.Mock;
const mockedPush = jest.fn();
let RouterWrapper;

describe('Home', () => {
  beforeAll(() => {
    mockedPush.mockImplementation(() => Promise.resolve());
    const MockedRouterContext = RouterContext as React.Context<unknown>;
    RouterWrapper = ({ children }): JSX.Element => {
      return (
        <MockedRouterContext.Provider
          value={{
            push: mockedPush,
          }}
        >
          {children}
        </MockedRouterContext.Provider>
      );
    };

    mockedPrismic.mockReturnValue({
      query: () => {
        return Promise.resolve(mockedQueryReturn);
      },
    });

    mockedFetch.mockImplementation(() => {
      return Promise.resolve({
        json: () =>
          Promise.resolve({
            next_page: null,
            results: [
              {
                uid: 'criando-um-app-cra-do-zero',
                first_publication_date: '2021-03-25T19:27:35+0000',
                data: {
                  title: 'Criando um app CRA do zero',
                  subtitle:
                    'Tudo sobre como criar a sua primeira aplicação utilizando Create React App',
                  author: 'Danilo Vieira',
                },
              },
            ],
          }),
      });
    });
  });

  it('should be able to return prismic posts documents using getStaticProps', async () => {
    const postsPaginationReturn = mockedQueryReturn;

    const getStaticPropsContext: GetStaticPropsContext<ParsedUrlQuery> = {};

    const response = (await getStaticProps(
      getStaticPropsContext
    )) as GetStaticPropsResult;

    expect(response.props.postsPagination).toEqual(postsPaginationReturn);
  });

  it('should be able to render logo', () => {
    const postsPagination = mockedQueryReturn;

    render(<App postsPagination={postsPagination} />);

    screen.getByAltText('logo');
  });

  it('should be able to render posts documents info', () => {
    const postsPagination = mockedQueryReturn;

    render(<App postsPagination={postsPagination} />);

    screen.getByText('Como utilizar Hooks');
    screen.getByText('Pensando em sincronização em vez de ciclos de vida');
    screen.getByText('15 mar 2021');
    screen.getByText('Joseph Oliveira');

    screen.getByText('Criando um app CRA do zero');
    screen.getByText(
      'Tudo sobre como criar a sua primeira aplicação utilizando Create React App'
    );
    screen.getByText('15 mar 2021');
    screen.getByText('Danilo Vieira');
  });

  it('should be able to navigate to post page after a click', () => {
    const postsPagination = mockedQueryReturn;

    render(<App postsPagination={postsPagination} />, {
      wrapper: RouterWrapper,
    });

    const firstPostTitle = screen.getByText('Como utilizar Hooks');
    const secondPostTitle = screen.getByText('Criando um app CRA do zero');

    fireEvent.click(firstPostTitle);
    fireEvent.click(secondPostTitle);

    expect(mockedPush).toHaveBeenNthCalledWith(
      1,
      '/post/como-utilizar-hooks',
      expect.anything(),
      expect.anything()
    );
    expect(mockedPush).toHaveBeenNthCalledWith(
      2,
      '/post/criando-um-app-cra-do-zero',
      expect.anything(),
      expect.anything()
    );
  });

  it('should be able to load more posts if available', async () => {
    const postsPagination = { ...mockedQueryReturn };
    postsPagination.results = [
      {
        uid: 'como-utilizar-hooks',
        first_publication_date: '2021-03-15T19:25:28+0000',
        data: {
          title: 'Como utilizar Hooks',
          subtitle: 'Pensando em sincronização em vez de ciclos de vida',
          author: 'Joseph Oliveira',
        },
      },
    ];

    render(<App postsPagination={postsPagination} />);

    screen.getByText('Como utilizar Hooks');
    const loadMorePostsButton = screen.getByText('Carregar mais posts');

    fireEvent.click(loadMorePostsButton);

    await waitFor(
      () => {
        expect(mockedFetch).toHaveBeenCalled();
      },
      { timeout: 200 }
    );

    screen.getByText('Criando um app CRA do zero');
  });

  it('should not be able to load more posts if not available', async () => {
    const postsPagination = mockedQueryReturn;
    postsPagination.next_page = null;

    render(<App postsPagination={postsPagination} />);

    screen.getByText('Como utilizar Hooks');
    screen.getByText('Criando um app CRA do zero');
    const loadMorePostsButton = screen.queryByText('Carregar mais posts');

    expect(loadMorePostsButton).not.toBeInTheDocument();
  });
});