import expect from 'expect';

import createTestState from 'mtest/helpers/createTestState';

import { Action } from 'mweb/common/actions/root';
import {
  app,
  Platform,
  Location,
  ErrorCondition,
} from 'mweb/common/reducers/app';
import {
  GAMES_DATA_PAGE_LOADED_ACTION_TYPE,
  GAMES_DATA_PAGE_FAILED_ACTION_TYPE,
} from 'mweb/common/actions/data/games';
import {
  CHANNELS_DATA_PAGE_LOADED_ACTION_TYPE,
  CHANNELS_DATA_CHANNEL_LOADED_ACTION_TYPE,
  CHANNELS_DATA_PAGE_FAILED_ACTION_TYPE,
  CHANNELS_DATA_CHANNEL_FAILED_ACTION_TYPE,
} from 'mweb/common/actions/data/channels';
import {
  VODS_DATA_VOD_LOADED_ACTION_TYPE,
  VODS_DATA_VOD_FAILED_ACTION_TYPE,
} from 'mweb/common/actions/data/vods';
import {
  EVENTS_DATA_EVENT_LOADED_ACTION_TYPE,
  EVENTS_DATA_EVENT_FAILED_ACTION_TYPE,
  EventsDataEventFailedAction,
} from 'mweb/common/actions/data/events';
import { OS, Browser } from 'mweb/common/reducers/device';
import {
  NetInfo,
  PLATFORM_SWITCHED_TO_CLIENT_ACTION_TYPE,
  PLATFORM_SWITCHED_TO_SERVER_ACTION_TYPE,
} from 'mweb/common/actions/platform';
import {
  ERROR_UNHANDLED_ERROR_OCCURRED_ACTION_TYPE,
  ERROR_ERROR_STATUS_RESET_ACTION_TYPE,
} from 'mweb/common/actions/error';
import {
  NAVIGATION_UPDATED_LOCATION_ACTION_TYPE,
  NAVIGATION_SWITCHED_TO_DESKTOP_ACTION_TYPE,
  NAVIGATION_LOADED_BRANCH_DEEPLINK_URL,
} from 'mweb/common/actions/navigation';
import { MOBILE_OPT_OUT_COOKIE_NAME } from 'mweb/common/utils/cookie';
import { GQLErrorType } from 'mweb/common/fetch/fetchGQL';

describe('app reducer', () => {
  it('sets reasonable defaults', () => {
    expect(app(undefined, { type: 'NOOP' })).toEqual({
      mobileOptOut: '',
      status: 200,
      location: 'unknown',
      url: '',
      platform: Platform.Unknown,
      gitHash: '',
    });
  });

  describe('platform actions', () => {
    it('handles switching to server platform and records gitHash', () => {
      const action: Action = {
        type: PLATFORM_SWITCHED_TO_SERVER_ACTION_TYPE,
        payload: {
          gitHash: 'cornedbeef',
          spadeURI: '//somewhere.over.the.rainbow',
        },
      };
      const newState = app(undefined, action);
      expect(newState.platform).toEqual(Platform.Server);
      expect(newState.gitHash).toEqual('cornedbeef');
    });

    it('handles switching to client platform', () => {
      const initialState = createTestState({
        app: { platform: Platform.Server },
      }).app;
      const action: Action = {
        type: PLATFORM_SWITCHED_TO_CLIENT_ACTION_TYPE,
        payload: {
          deviceID: 'abcdef',
          sessionID: 'hijkl',
          os: OS.Other,
          browser: Browser.Other,
          netInfo: {} as NetInfo,
        },
      };
      expect(app(initialState, action).platform).toEqual(Platform.Client);
    });
  });

  describe('error actions', () => {
    it('sets the status to 500 for unhandled error actions', () => {
      const action: Action = {
        type: ERROR_UNHANDLED_ERROR_OCCURRED_ACTION_TYPE,
      };
      expect(app(undefined, action).status).toEqual(500);
    });

    it('sets errorCondition to ApiTimeout for a payload with a RequestTimeout ', () => {
      const action: Action = {
        type: EVENTS_DATA_EVENT_FAILED_ACTION_TYPE,
        payload: { type: GQLErrorType.RequestTimeout },
      } as EventsDataEventFailedAction;
      expect(app(undefined, action).errorCondition).toEqual(
        ErrorCondition.ApiTimeout,
      );
    });

    it('sets errorCondition to ApiTimeout for a payload with a BodyTimeout ', () => {
      const action: Action = {
        type: EVENTS_DATA_EVENT_FAILED_ACTION_TYPE,
        payload: { type: GQLErrorType.BodyTimeout },
      } as EventsDataEventFailedAction;
      expect(app(undefined, action).errorCondition).toEqual(
        ErrorCondition.ApiTimeout,
      );
    });

    it('resets the status on transition', () => {
      const initialState = createTestState({
        app: {
          status: 9000,
        },
      }).app;
      const action: Action = {
        type: ERROR_ERROR_STATUS_RESET_ACTION_TYPE,
      };
      expect(app(initialState, action).status).toEqual(200);
    });
  });

  describe('navigation actions', () => {
    it('sets the location', () => {
      const action: Action = {
        type: NAVIGATION_UPDATED_LOCATION_ACTION_TYPE,
        payload: {
          location: Location.Channel,
          referrer: 'http://www.twitch.tv/',
        },
      };

      const newState = app(undefined, action);
      expect(newState.location).toEqual(Location.Channel);
    });

    it('stores the mobileOptOut flag on switch to desktop action', () => {
      const action: Action = {
        type: NAVIGATION_SWITCHED_TO_DESKTOP_ACTION_TYPE,
        payload: {
          cookieName: MOBILE_OPT_OUT_COOKIE_NAME,
        },
      };
      expect(app(undefined, action).mobileOptOut).toEqual(
        MOBILE_OPT_OUT_COOKIE_NAME,
      );
    });

    it('stores the branch deeplink url on loaded action', () => {
      const action: Action = {
        type: NAVIGATION_LOADED_BRANCH_DEEPLINK_URL,
        payload: {
          url: 'www.alec-lee.com',
        },
      };
      expect(app(undefined, action).branchDeeplinkURL).toEqual(
        'www.alec-lee.com',
      );
    });
  });

  describe('load success actions', () => {
    const tests = [
      GAMES_DATA_PAGE_LOADED_ACTION_TYPE,
      CHANNELS_DATA_PAGE_LOADED_ACTION_TYPE,
      CHANNELS_DATA_CHANNEL_LOADED_ACTION_TYPE,
      VODS_DATA_VOD_LOADED_ACTION_TYPE,
      EVENTS_DATA_EVENT_LOADED_ACTION_TYPE,
    ];

    tests.forEach(type => {
      it(`sets status to 200 on successful loads of type ${type}`, () => {
        const initialState = createTestState({
          app: { status: 500 },
        }).app;
        const action = { type } as Action;

        expect(app(initialState, action).status).toEqual(200);
      });
    });
  });

  describe('load failure actions', () => {
    const tests = [
      GAMES_DATA_PAGE_FAILED_ACTION_TYPE,
      CHANNELS_DATA_PAGE_FAILED_ACTION_TYPE,
      CHANNELS_DATA_CHANNEL_FAILED_ACTION_TYPE,
      VODS_DATA_VOD_FAILED_ACTION_TYPE,
      EVENTS_DATA_EVENT_FAILED_ACTION_TYPE,
    ];

    const statuses = [503, 404];

    tests.forEach(type => {
      statuses.forEach(status => {
        it(`sets the error status to ${status} for load failures from ${type}`, () => {
          const action = {
            type,
            payload: { status: status },
          } as Action;
          expect(app(undefined, action).status).toEqual(status);
        });
      });
    });
  });
});
