import { gql } from '@apollo/client';
import React from 'react';
import { act, create } from 'react-test-renderer';
import { MockedProvider } from '@apollo/client/testing';
import {
  ChartStateLoading as Loading,
  EmptyState,
} from '../index';
import GraphQLWrapper, { formatQueryVariables } from './index';

const resultData = {
  analyticsTotals: {
    organic: {
      clicks: 4616,
      likes: 4085,
      impressions: 5592,
    },
  },
};

const QUERY = gql`
  query Query(
    $serviceId: String!
    $serviceType: String!
    $startDate: Date!
    $endDate: Date!
  ) {
    analyticsTotals(
      serviceId:$serviceId,
      serviceType:$serviceType,
      startDate:$startDate,
      endDate:$endDate,
    ) {
      organic{
        clicks,
        followers,
        impressions,
      },
    }
  }
`;

const mock = {
  request: {
    query: QUERY,
    variables: {
      serviceId: 'linkedin42',
      serviceType: 'linkedin',
      startDate: '2020-10-01',
      endDate: '2020-10-10',
    },
  },
  result: {
    data: resultData,
  },
};

const errorMock = {
  request: {
    query: QUERY,
    variables: {
      serviceId: 'linkedin42',
      serviceType: 'linkedin',
      startDate: '2020-10-01',
      endDate: '2020-10-10',
    },
  },
  error: new Error('Ouch!'),
};

const variables = formatQueryVariables({
  serviceId: 'linkedin42',
  service: 'linkedin',
}, {
  startDate: '10/01/2020',
  endDate: '10/10/2020',
});


describe('GraphQLWrapper', () => {
  it('should show loading', () => {
    const component = create(<MockedProvider mocks={[mock]} addTypename={false}>
      <GraphQLWrapper
        graphQlProps={{
          query: QUERY,
          variables,
        }}
        save={() => {}}
      />
    </MockedProvider>);

    component.root.find(Loading);
  });

  it('should show Error', async () => {
    let component;

    act(() => {
      component = create(<MockedProvider mocks={[errorMock]} addTypename={false}>
        <GraphQLWrapper
          graphQlProps={{
            query: QUERY,
            variables,
          }}
          save={() => {}}
        />
      </MockedProvider>);
    });

    await act(() => new Promise(resolve => setTimeout(resolve, 0)));
    component.root.findByType(EmptyState);
  });

  it('should render content', async () => {
    const Content = () => (
      <div id="content">Content!</div>
    );
    let component;

    act(() => {
      component = create(<MockedProvider mocks={[mock]} addTypename={false}>
        <GraphQLWrapper
          graphQlProps={{
            query: QUERY,
            variables,
            content: Content,
          }}
          save={() => {}}
        />
      </MockedProvider>);
    });

    await act(() => new Promise(resolve => setTimeout(resolve, 0)));
    component.root.findByType(Content);
  });

  it('pass down data as metrics', async () => {
    const Content = () => (
      <div id="content">Content!</div>
    );
    let component;

    act(() => {
      component = create(<MockedProvider mocks={[mock]} addTypename={false}>
        <GraphQLWrapper
          graphQlProps={{
            query: QUERY,
            variables,
            content: Content,
          }}
          save={() => {}}
        />
      </MockedProvider>);
    });

    await act(() => new Promise(resolve => setTimeout(resolve, 0)));
    expect(component.root.findByType(Content).props.metrics)
      .toMatchObject(resultData);
  });

  it('process the data and pass it down as metrics', async () => {
    const Content = () => (
      <div id="content">Content!</div>
    );
    let component;

    function dataParser(data) {
      return {
        ...data,
        parsed: true,
      };
    }

    act(() => {
      component = create(<MockedProvider mocks={[mock]} addTypename={false}>
        <GraphQLWrapper
          graphQlProps={{
            query: QUERY,
            variables,
            dataParser,
            content: Content,
          }}
          save={() => {}}
        />
      </MockedProvider>);
    });

    await act(() => new Promise(resolve => setTimeout(resolve, 0)));
    expect(component.root.findByType(Content).props)
      .toMatchObject(dataParser(resultData));
  });

  it('saves result data', async () => {
    const Content = () => (
      <div id="content">Content!</div>
    );

    const save = jest.fn();

    act(() => {
      create(<MockedProvider mocks={[mock]} addTypename={false}>
        <GraphQLWrapper
          graphQlProps={{
            query: QUERY,
            variables,
            content: Content,
            save,
          }}
        />
      </MockedProvider>);
    });

    await act(() => new Promise(resolve => setTimeout(resolve, 0)));
    expect(save).toHaveBeenCalledTimes(1);
    expect(save).toHaveBeenCalledWith(resultData);
  });
});
