import expect from 'expect';
import fetchMock from 'fetch-mock';

import {
  CHANNEL,
  CHANNEL_DATA_PAYLOAD,
  mockChannel,
  mockStatusNotFound,
  mockChannelWithoutSubs,
} from 'mtest/fetchMocks/channel';
import {
  ALL_GAMES_CHANNEL_DETAILS_MAP,
  GAME_CHANNEL_DETAILS_MAP,
  GAME_JSON_CURSOR,
  mockChannelsForGame,
  mockChannelsAllGames,
  mockChannelsForGamePage2,
  mockChannelsNotGame,
  NOT_GAME_ALIAS,
} from 'mtest/fetchMocks/channels';
import { GAME_1 } from 'mtest/fetchMocks/games';

import {
  CHANNEL_PAGE_SIZE,
  compileFetchChannelOperation,
  compileFetchChannelsOperation,
  fetchChannel,
  fetchChannels,
} from 'mweb/common/fetch/channels';
import {
  ALL_CHANNELS,
  ChannelSubscribableStatus,
} from 'mweb/common/reducers/data/channels';

describe('fetchChannel', () => {
  afterEach(() => fetchMock.restore());

  describe('when a channel exists', () => {
    beforeEach(mockChannel);

    it('fetches a channel and returns the details', () => {
      return fetchChannel(CHANNEL).then(result => {
        expect(
          JSON.parse((fetchMock.calls().matched[0][1] as any).body),
        ).toEqual({
          query: compileFetchChannelOperation(),
          operationName: 'ChannelQuery',
          variables: {
            login: CHANNEL,
            withArchives: false,
            withRecentClips: false,
            withTopClips: false,
            withRecentHighlights: false,
            withTopHighlights: false,
            withRecentPremieres: false,
            withRecentUploads: false,
          },
        });

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

    it('fetches a channel for a profile usage and handles related content counts', () => {
      return fetchChannel(CHANNEL, {
        archiveCount: 7,
        recentClipCount: 6,
        recentHighlightCount: 5,
        topClipCount: 1,
        topHighlightCount: 2,
        recentPremiereCount: 9,
        recentUploadCount: 8,
      }).then(result => {
        expect(
          JSON.parse((fetchMock.calls().matched[0][1] as any).body),
        ).toEqual({
          query: compileFetchChannelOperation(),
          operationName: 'ChannelQuery',
          variables: {
            login: CHANNEL,
            withArchives: true,
            archiveCount: 7,
            withRecentClips: true,
            recentClipCount: 6,
            withTopClips: true,
            topClipCount: 1,
            withRecentHighlights: true,
            recentHighlightCount: 5,
            withTopHighlights: true,
            topHighlightCount: 2,
            withRecentPremieres: true,
            recentPremiereCount: 9,
            withRecentUploads: true,
            recentUploadCount: 8,
          },
        });
        expect(result).toEqual(CHANNEL_DATA_PAYLOAD);
      });
    });
  });

  describe('when a channel does have subscription products', () => {
    beforeEach(mockChannel);
    it('correctly marks it has sub products', () => {
      return fetchChannel(CHANNEL).then(result => {
        expect(result!.channel.subscribableStatus).toEqual(
          ChannelSubscribableStatus.CanSubscribe,
        );
      });
    });
  });

  describe('when a channel does not have subscription products', () => {
    beforeEach(mockChannelWithoutSubs);
    it('correctly marks no sub products', () => {
      return fetchChannel(CHANNEL).then(result => {
        expect(result!.channel.subscribableStatus).toEqual(
          ChannelSubscribableStatus.CannotSubscribe,
        );
      });
    });
  });

  describe('when a channel does not exist', () => {
    beforeEach(mockStatusNotFound);
    it('returns null', () => {
      return fetchChannel(CHANNEL).then(result => {
        expect(result).toEqual(undefined);
      });
    });
  });
});
describe('fetchChannels', () => {
  afterEach(() => fetchMock.restore());
  describe('with results', () => {
    beforeEach(mockChannelsForGame);

    it('fetches channels and returns the details', () => {
      const CURSOR = 'aaaa';
      const gameAlias = GAME_1.name.toUpperCase();
      return fetchChannels({
        gameAlias,
        cursor: CURSOR,
      }).then(result => {
        expect(
          JSON.parse((fetchMock.calls().matched[0][1] as any).body),
        ).toEqual({
          query: compileFetchChannelsOperation(),
          operationName: 'ChannelsQuery',
          variables: {
            gameAlias,
            count: CHANNEL_PAGE_SIZE,
            cursor: CURSOR,
            hasGame: true,
          },
        });
        expect(result).toEqual({
          channelDetails: GAME_CHANNEL_DETAILS_MAP,
          cursor: GAME_JSON_CURSOR,
          game: GAME_1,
          gameAliasUsed: gameAlias,
        });
      });
    });
  });

  describe('with no results', () => {
    beforeEach(mockChannelsForGamePage2);

    it('fetches no channels and returns no details', () => {
      const CURSOR = 'aaaa';
      const gameAlias = GAME_1.name.toUpperCase();
      return fetchChannels({
        gameAlias,
        cursor: CURSOR,
      }).then(result => {
        expect(
          JSON.parse((fetchMock.calls().matched[0][1] as any).body),
        ).toEqual({
          query: compileFetchChannelsOperation(),
          operationName: 'ChannelsQuery',
          variables: {
            gameAlias,
            count: CHANNEL_PAGE_SIZE,
            cursor: CURSOR,
            hasGame: true,
          },
        });
        expect(result).toEqual({
          channelDetails: {},
          cursor: null,
          game: GAME_1,
          gameAliasUsed: gameAlias,
        });
      });
    });
  });

  describe('fetching all games', () => {
    beforeEach(mockChannelsAllGames);

    it('handles the all games response', () => {
      return fetchChannels({
        cursor: null,
        gameAlias: ALL_CHANNELS,
      }).then(result => {
        expect(
          JSON.parse((fetchMock.calls().matched[0][1] as any).body),
        ).toEqual({
          query: compileFetchChannelsOperation(),
          operationName: 'ChannelsQuery',
          variables: {
            count: CHANNEL_PAGE_SIZE,
            hasGame: false,
            cursor: null,
          },
        });
        expect(result).toEqual({
          channelDetails: ALL_GAMES_CHANNEL_DETAILS_MAP,
          cursor: null,
          game: undefined,
          gameAliasUsed: ALL_CHANNELS,
        });
      });
    });
  });

  describe('with a bad game title', () => {
    beforeEach(mockChannelsNotGame);

    it('fetches no channels and returns no details', () => {
      const CURSOR = 'aaaa';
      return fetchChannels({
        gameAlias: NOT_GAME_ALIAS,
        cursor: CURSOR,
      }).then(result => {
        expect(
          JSON.parse((fetchMock.calls().matched[0][1] as any).body),
        ).toEqual({
          query: compileFetchChannelsOperation(),
          operationName: 'ChannelsQuery',
          variables: {
            gameAlias: NOT_GAME_ALIAS,
            count: CHANNEL_PAGE_SIZE,
            cursor: CURSOR,
            hasGame: true,
          },
        });
        expect(result).toEqual({
          channelDetails: {},
          cursor: null,
          game: undefined,
          gameAliasUsed: NOT_GAME_ALIAS,
        });
      });
    });
  });
});
