/* global describe: true, test:true, expect:true */
import { frames as framesReducer, initialState } from '~/reducers/frames';
import Factory from 'helper/factories/api';
import {
	GET_FRAMES,
	GET_FRAMES_SUCCESS,
	GET_FRAMES_ERROR,
	LOGOUT_SUCCESS,
	GET_FRAME_STATE_SUCCESS,
	GET_FRAME_STATE_ERROR,
	SET_CURRENT_FRAME_PLAYLIST,
	GET_FRAMES_STATUS_SUCCESS,
	SET_FRAME_POWER_STATE_SUCCESS,
	SET_CURRENT_FRAME,
	UPDATE_FRAME_PLAYLIST_ASSIGNMENT,
	GET_FRAME_STATE,
	UNPAIR_FRAME,
	UNPAIR_FRAME_SUCCESS,
	UNPAIR_FRAME_ERROR,
} from '~/store/actionTypes';
import { FRAME_ORIENTATION, FRAME_POWER } from '~/store/frameConstants';

describe('frames reducer', () => {
	test('CASE 1: should return initial state', () => {
		expect(framesReducer(undefined, { type: 'Default' }))
			.toEqual(initialState);
	});

	test('CASE 2: should return state with meta set to loading when type is GET_FRAMES', () => {
		const frames = [{ id: 1 }, { id: 2 }];
		const expectedState = { ...initialState, meta: { ...initialState.meta, loading: true } };
		const stateResult = framesReducer(initialState, {
			type: GET_FRAMES, payload: { frames },
		});
		expect(stateResult).toEqual(expectedState);
	});

	test('CASE 3a: should return with removed frame with existing frameState when type is GET_FRAMES_SUCCESS', () => {
		const frames = [{
			id: 1,
		}];
		const currentState = {
			framePreventPlaylistAssignChange: {},
			frames: ['1ea790afd4c67c77', 'U6XYMRIEWBJ82RLL'].map((f, i) => (
				{ id: i, ...Factory.build('FrameState', { frameId: f }) })),
		};
		const meta = { loading: false, error: '' };
		const specs = { 'W08A-01': { colors: [], photo: {} } };
		const stateResult = framesReducer(currentState, {
			type: GET_FRAMES_SUCCESS, payload: { frames, specs },
		});
		expect(stateResult.frames[0]).toHaveProperty('meta', meta);
		expect(stateResult.frames[0]).toHaveProperty('orientation');
		expect(stateResult.frames[0]).toHaveProperty('power');
		expect(stateResult.frames).toHaveLength(frames.length);
	});

	test('CASE 3b: should return with added frame with existing frameState and needUpdate as true when type is GET_FRAMES_SUCCESS', () => {
		const frames = [
			{ id: 1, softwareVersion: '5.0.0' },
			{ id: 2 },
			{ id: 3 },
		];
		const currentState = {
			framePreventPlaylistAssignChange: {},
			frames: ['1ea790afd4c67c77', 'U6XYMRIEWBJ82RLL'].map((f, i) => (
				{ id: i, softwareVersion: '5.0.0', ...Factory.build('FrameState', { frameId: f }) })),
		};

		const meta = { loading: false, error: '' };
		const specs = { 'W08A-01': { colors: [], photo: {}, minVersionSupported: '6.0.0' } };
		const stateResult = framesReducer(currentState, {
			type: GET_FRAMES_SUCCESS, payload: { frames, specs },
		});
		expect(stateResult.frames[0]).toHaveProperty('meta', meta);
		expect(stateResult.frames[0]).toHaveProperty('orientation');
		expect(stateResult.frames[0]).toHaveProperty('power');
		expect(stateResult.frames[0]).toHaveProperty('needUpdate', true);
		expect(stateResult.frames).toHaveLength(frames.length);
	});

	test('CASE 3c: should return with added frame with existing frameState and needUpdate set to false when type is GET_FRAMES_SUCCESS and checkEnabled is false', () => {
		const frames = [
			{ id: 1, softwareVersion: '5.0.0' },
			{ id: 2 },
			{ id: 3 },
		];
		const currentState = {
			framePreventPlaylistAssignChange: {},
			frames: ['1ea790afd4c67c77', 'U6XYMRIEWBJ82RLL'].map((f, i) => (
				{ id: i, softwareVersion: '5.0.0', ...Factory.build('FrameState', { frameId: f }) })),
		};

		const meta = { loading: false, error: '' };
		const specs = { 'W08A-01': { colors: [], photo: {}, minVersionSupported: '6.0.0' } };
		const stateResult = framesReducer(currentState, {
			type: GET_FRAMES_SUCCESS, payload: { frames, specs, checkEnabled: false },
		});
		expect(stateResult.frames[0]).toHaveProperty('meta', meta);
		expect(stateResult.frames[0]).toHaveProperty('orientation');
		expect(stateResult.frames[0]).toHaveProperty('power');
		expect(stateResult.frames[0]).toHaveProperty('needUpdate', false);
		expect(stateResult.frames).toHaveLength(frames.length);
	});

	test('CASE 5: should return state with error and loading = `false` when type is GET_FRAMES_ERROR', () => {
		const error = new Error('getFramesError');
		const expectedState = {
			...initialState,
			meta:
			{ ...initialState.meta, error, loading: false },
		};
		const stateResult = framesReducer(initialState, {
			type: GET_FRAMES_ERROR, payload: { error },
		});
		expect(stateResult).toEqual(expectedState);
	});

	test('CASE 6: should empty the state on LOGOUT_SUCCESS', () => {
		const existingState = [{ id: 1 }, { id: 2 }];
		expect(framesReducer(existingState, { type: LOGOUT_SUCCESS }))
			.toEqual(initialState);
	});

	test('CASE 7a: should return state with frameState on GET_FRAME_STATE_SUCCESS', () => {
		const playlistId = 321;
		const existingState = { framePreventPlaylistPlayingChange: {}, framePreventPowerChange: {}, frames: [{ id: 1, frameId: '1ea790afd4c67c77', playlists: [{ id: playlistId }] }, { id: 2 }] };
		const expectedState = {
			framePreventPlaylistPlayingChange: {},
			framePreventPowerChange: {},
			frames: [
				{
					id: 1,
					frameId: '1ea790afd4c67c77',
					orientation: FRAME_ORIENTATION.LANDSCAPE,
					slideshow: playlistId,
					playlists: [{ id: playlistId }],
					power: 'on',
				},
				{ id: 2 },
			],
			meta: { stateLoading: false, error: '' },
		};

		const frameState = Factory.build('FrameState', { frameId: existingState.frames[0].frameId, slideshow: { value: playlistId } });
		expect(framesReducer(existingState, { type: GET_FRAME_STATE_SUCCESS, payload: { frameState } }))
			.toEqual(expectedState);
	});

	test('CASE 7b: should return state with slideshow=`-1` on GET_FRAME_STATE_SUCCESS when playlist is no longer assigned', () => {
		const playlistId = 321;
		const existingState = { framePreventPlaylistPlayingChange: {}, framePreventPowerChange: {}, frames: [{ id: 1, frameId: '1ea790afd4c67c77', playlists: [] }, { id: 2 }] };
		const expectedState = {
			framePreventPlaylistPlayingChange: {},
			framePreventPowerChange: {},
			frames: [
				{
					id: 1,
					frameId: '1ea790afd4c67c77',
					orientation: FRAME_ORIENTATION.LANDSCAPE,
					slideshow: -1,
					playlists: [],
					power: 'on',
				},
				{ id: 2 },
			],
			meta: { stateLoading: false, error: '' },
		};

		const frameState = Factory.build('FrameState', { frameId: existingState.frames[0].frameId, slideshow: { value: playlistId } });
		expect(framesReducer(existingState, { type: GET_FRAME_STATE_SUCCESS, payload: { frameState } }))
			.toEqual(expectedState);
	});

	test('CASE 7c: should return state with slideshow=`-1` on GET_FRAME_STATE_SUCCESS when frameState.slideshow is `null`', () => {
		const playlistId = 'null';
		const existingState = { framePreventPlaylistPlayingChange: {}, framePreventPowerChange: {}, frames: [{ id: 1, frameId: '1ea790afd4c67c77', playlists: [] }, { id: 2 }] };
		const expectedState = {
			framePreventPlaylistPlayingChange: {},
			framePreventPowerChange: {},
			frames: [
				{
					id: 1,
					frameId: '1ea790afd4c67c77',
					orientation: FRAME_ORIENTATION.LANDSCAPE,
					slideshow: -1,
					playlists: [],
					power: 'on',
				},
				{ id: 2 },
			],
			meta: { stateLoading: false, error: '' },
		};

		const frameState = Factory.build('FrameState', { frameId: existingState.frames[0].frameId, slideshow: { value: playlistId } });
		expect(framesReducer(existingState, { type: GET_FRAME_STATE_SUCCESS, payload: { frameState } }))
			.toEqual(expectedState);
	});

	test('CASE 8: should return state with corrent frame playlist on SET_CURRENT_FRAME_PLAYLIST', () => {
		const existingState = {
			frames: [
				{
					id: 1,
					frameId: '1ea790afd4c67c77',
					orientation: FRAME_ORIENTATION.LANDSCAPE,
					slideshow: 123,
					power: 'on',
				},
				{ id: 2 },
			],
		};

		const payload = {
			framePk: 1,
			playlistId: 234,
		};
		expect(framesReducer(existingState, { type: SET_CURRENT_FRAME_PLAYLIST, payload }).frames[0])
			.toHaveProperty('slideshow', 234);
	});

	test('CASE 9: should return state with correct frameState on GET_FRAMES_STATUS_SUCCESS', () => {
		const framesStatus = {
			frames: [
				{ lastSeen: null, lastConnected: null, framePk: 17544 },
				{ lastSeen: 1535338881548, lastConnected: 1535338819374, framePk: 18122 },
				{
					connected: true, lastSeen: 1539065938946, lastConnected: 1539065965182, framePk: 18429,
				},
				{
					connected: true, lastSeen: 1539066031822, lastConnected: 1539066063638, framePk: 18438,
				},
			],
		};

		const currentState = {
			frames: [17544, 18122, 18429, 18438, 18433].map((f) => (
				{ id: f, ...Factory.build('FrameState') })),
		};

		const payload = { framesStatus };
		const newState = framesReducer(currentState, { type: GET_FRAMES_STATUS_SUCCESS, payload });
		expect(newState.frames)
			.toEqual(expect.arrayContaining(framesStatus.frames.map(() =>
				expect.objectContaining({ online: expect.any(Boolean) }))));
	});

	test('CASE 10: should return state with frameState on SET_FRAME_POWER_STATE_SUCCESS', () => {
		const emptyObject = {};
		const currentState = {
			framePreventPowerChange: { 1: true },
		};

		const payload = { framePk: 1, powerState: FRAME_POWER.OFF };
		const newState = framesReducer(currentState, { type: SET_FRAME_POWER_STATE_SUCCESS, payload });
		expect(newState)
			.toHaveProperty('framePreventPowerChange', emptyObject);
	});

	test('CASE 11: should return currentFrameId set to the payload.id when action is SET_CURRENT_FRAME', () => {
		const currentState = {
			frames: [
				{ id: 1 },
				{ id: 2 },
			],
			currentFrameId: 2,
		};

		const payload = { id: 1 };
		const newState = framesReducer(currentState, { type: SET_CURRENT_FRAME, payload });
		expect(newState)
			.toHaveProperty('currentFrameId', 1);
	});

	test('CASE 12: should return state with loading = `false` on GET_FRAME_STATE_ERROR', () => {
		const currentState = {
			framePreventPlaylistAssignChange: {},
			framePreventPlaylistPlayingChange: {},
			framePreventPowerChange: {},
			frames: [],
			currentFrameId: 0,
			meta: {
				loading: true,
				stateLoading: false,
				statusLoading: false,
				error: '',
			},
		};

		const newState = framesReducer(initialState, { type: GET_FRAME_STATE_ERROR });
		expect(newState)
			.toEqual(currentState);
	});

	test('CASE 13a: should return state with updated playlists when operation = `add` on UPDATE_FRAME_PLAYLIST_ASSIGNMENT', () => {
		const frameIds = [17544, 18122, 18429, 18438, 18433];
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			currentFrameId: 0,
			meta: { loading: false, error: '' },
		};
		const playlistIds = [103];
		const payload = { frameIds, playlistIds, operation: 'add' };
		const newState = framesReducer(
			currentState,
			{ type: UPDATE_FRAME_PLAYLIST_ASSIGNMENT, payload },
		);
		expect(newState.frames)
			.toEqual(expect.arrayContaining([
				expect.objectContaining({
					playlists: [
						{ id: expect.any(Number) },
						{ id: expect.any(Number) },
						{ id: expect.any(Number) },
					],
				})]));
	});

	test('CASE 13b: should return state with removed playlists when operation = `remove` on UPDATE_FRAME_PLAYLIST_ASSIGNMENT', () => {
		const frameIds = [17544, 18122, 18429, 18438, 18433];
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			currentFrameId: 0,
			meta: { loading: false, error: '' },
		};
		const playlistIds = [currentState.frames[0].playlists[0].id];
		const payload = { frameIds, playlistIds, operation: 'remove' };
		const newState = framesReducer(
			currentState,
			{ type: UPDATE_FRAME_PLAYLIST_ASSIGNMENT, payload },
		);
		expect(newState.frames)
			.toEqual(expect.arrayContaining([
				expect.objectContaining({
					playlists: [
						{ id: expect.any(Number) },
					],
				})]));
	});

	test('CASE 13c: should return state with new playlists when operation is not specified on UPDATE_FRAME_PLAYLIST_ASSIGNMENT', () => {
		const frameIds = [17544, 18122, 18429, 18438, 18433];
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			currentFrameId: 0,
			meta: { loading: false, error: '' },
		};
		const playlistIds = [111];
		const payload = { frameIds, playlistIds };
		const newState = framesReducer(
			currentState,
			{ type: UPDATE_FRAME_PLAYLIST_ASSIGNMENT, payload },
		);
		expect(newState.frames)
			.toEqual(expect.arrayContaining([
				expect.objectContaining({
					playlists: [
						{ id: playlistIds[0] },
					],
				})]));
	});

	test('CASE 14: should return state loading = `false` when action is GET_FRAME_STATE', () => {
		const frameIds = [17544, 18122, 18429, 18438, 18433];
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			currentFrameId: 0,
			meta: { error: '' },
		};
		const newState = framesReducer(
			currentState,
			{ type: GET_FRAME_STATE },
		);
		expect(newState.meta)
			.toEqual({ stateLoading: true, error: '' });
	});

	test('CASE 15: should return state loading = `true` when action is UNPAIR_FRAME', () => {
		const frameIds = [17544, 18122, 18429, 18438, 18433];
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			currentFrameId: 0,
			meta: { error: '' },
		};
		const newState = framesReducer(
			currentState,
			{ type: UNPAIR_FRAME },
		);
		expect(newState.meta)
			.toEqual({ loading: true, error: '' });
	});
	test('CASE 16: should return state loading = `false` when action is UNPAIR_FRAME_SUCCESS', () => {
		const frameIds = [17544, 18122, 18429, 18438, 18433];

		const specs = { 'W08A-01': { colors: [], photo: {} } };
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			specs,
			currentFrameId: 0,
			meta: { error: '', loading: false },
		};
		const newState = framesReducer(
			currentState,
			{ type: UNPAIR_FRAME_SUCCESS, payload: { framePk: 0 } },
		);
		expect(newState.meta)
			.toEqual({ loading: false, error: '' });
	});
	test('CASE 17: should return state loading = `false` error message when action is UNPAIR_FRAME_ERROR', () => {
		const error = 'unpair frame error';
		const frameIds = [17544, 18122, 18429, 18438, 18433];
		const currentState = {
			frames: frameIds.map((f) => (
				Factory.build('Frame', { id: f })
			)),
			currentFrameId: 0,
			meta: { error: '' },
		};
		const newState = framesReducer(
			currentState,
			{ type: UNPAIR_FRAME_ERROR, payload: { error } },
		);
		expect(newState.meta)
			.toEqual({ loading: false, error });
	});
});
