/**
 * External dependencies
 */
import { combineReducers } from '@wordpress/data';
/**
 * Internal dependencies
 */
import {
	SET_IS_FETCHING_VIDEOS,
	SET_VIDEOS_FETCH_ERROR,
	SET_VIDEOS,
	SET_VIDEOS_QUERY,
	SET_VIDEOS_PAGINATION,
	SET_VIDEO,
	SET_VIDEO_PRIVACY,
	SET_IS_FETCHING_UPLOADED_VIDEO_COUNT,
	SET_UPLOADED_VIDEO_COUNT,
	SET_VIDEOS_STORAGE_USED,
	REMOVE_VIDEO,
	DELETE_VIDEO,
	UPLOADING_VIDEO,
	PROCESSING_VIDEO,
	UPLOADED_VIDEO,
	SET_IS_FETCHING_PURCHASES,
	SET_PURCHASES,
	UPDATE_VIDEO_PRIVACY,
	SET_LOCAL_VIDEOS,
	SET_LOCAL_VIDEOS_QUERY,
	SET_LOCAL_VIDEOS_PAGINATION,
	SET_IS_FETCHING_LOCAL_VIDEOS,
	SET_VIDEOS_FILTER,
	UPDATE_VIDEO_POSTER,
	SET_UPDATING_VIDEO_POSTER,
	SET_USERS,
	SET_USERS_PAGINATION,
	SET_LOCAL_VIDEO_UPLOADED,
	SET_IS_FETCHING_PLAYBACK_TOKEN,
	SET_PLAYBACK_TOKEN,
} from './constants';

/**
 * Retunr default query values
 *
 * @returns {object}       Full query object.
 */
export function getDefaultQuery() {
	return {
		order: 'desc',
		orderBy: 'date',
		itemsPerPage: 6,
		page: 1,
		type: 'video/videopress',
	};
}

const videos = ( state, action ) => {
	switch ( action.type ) {
		case SET_IS_FETCHING_VIDEOS: {
			return {
				...state,
				isFetching: action.isFetching,
			};
		}

		case SET_VIDEOS_FETCH_ERROR: {
			const { error } = action;
			return {
				...state,
				isFetching: false,
				error,
			};
		}

		case SET_VIDEOS_QUERY: {
			return {
				...state,
				query: {
					...state.query,
					...action.query,
				},
				_meta: {
					...state._meta,
					relyOnInitialState: false,
				},
			};
		}

		case SET_VIDEOS_PAGINATION: {
			return {
				...state,
				pagination: {
					...state.pagination,
					...action.pagination,
				},
				_meta: {
					...state._meta,
					relyOnInitialState: false,
				},
			};
		}

		case SET_VIDEOS_FILTER: {
			const { filter, value, isActive } = action;
			return {
				...state,
				filter: {
					...state.filter,
					[ filter ]: {
						...( state.filter?.[ filter ] || {} ),
						[ value ]: isActive,
					},
				},
				_meta: {
					...state._meta,
					relyOnInitialState: false,
				},
			};
		}

		case SET_VIDEOS: {
			const { videos: items } = action;
			return {
				...state,
				items,
				isFetching: false,
			};
		}

		case SET_VIDEO: {
			const { video } = action;
			const items = [ ...( state.items ?? [] ) ]; // Clone the array, to avoid mutating the state.
			const videoIndex = items.findIndex( item => item.id === video.id );

			if ( videoIndex === -1 ) {
				// Add video when not found at beginning of the list.
				items.unshift( video );
			} else {
				// Update video when found
				items[ videoIndex ] = {
					...items[ videoIndex ],
					...video,
				};
			}

			return {
				...state,
				isFetching: false,
				items,
			};
		}

		case SET_VIDEO_PRIVACY: {
			const { id, privacySetting } = action;
			const items = [ ...( state.items ?? [] ) ];
			const videoIndex = items.findIndex( item => item.id === id );

			if ( videoIndex < 0 ) {
				return state;
			}

			// current -> previous value of privacy
			const current = items[ videoIndex ].privacySetting;

			// Set privacy setting straigh in the state. Let's be optimistic.
			items[ videoIndex ] = {
				...items[ videoIndex ],
				privacySetting,
			};

			// Set metadata about the privacy change.
			const _metaItems = { ...( state._meta?.items ?? [] ) };
			const _metaVideo = _metaItems[ id ] ?? {};

			return {
				...state,
				items,
				_meta: {
					...state._meta,
					items: {
						..._metaItems,
						[ id ]: {
							..._metaVideo,
							isUpdatingPrivacy: true,
							hasBeenUpdatedPrivacy: false,
							prevPrivacySetting: current,
						},
					},
				},
			};
		}

		case UPDATE_VIDEO_PRIVACY: {
			const { id } = action;

			const _metaItems = { ...( state._meta?.items ?? [] ) };
			if ( ! _metaItems?.[ id ] ) {
				return state;
			}

			const _metaVideo = _metaItems[ id ] ?? {};

			return {
				...state,
				_meta: {
					...state._meta,
					items: {
						..._metaItems,
						[ id ]: {
							..._metaVideo,
							isUpdatingPrivacy: false,
							hasBeenUpdatedPrivacy: true,
							prevPrivacySetting: null,
						},
					},
				},
			};
		}

		/*
		 * REMOVE_VIDEO is the action trigger
		 * right after the user tries to remove the video,
		 * for instance, when the user clicks on the "Remove" button.
		 * Use it as an oportunity to update the UI and show a loading state,
		 * while the video is being removed.
		 */
		case REMOVE_VIDEO: {
			const { id } = action;
			const { items = [] } = state;
			const videoIndex = items.findIndex( item => item.id === id );

			if ( videoIndex < 0 ) {
				return state;
			}

			const _metaItems = {
				...( state._meta?.items ?? [] ),
			};

			const _metaVideo = _metaItems[ id ] ?? {};

			return {
				...state,
				// Do not remove the video from the list, just update the meta data.
				// Keep here in caswe we want to do it in the future.
				// items: [ ...state.items.slice( 0, videoIndex ), ...state.items.slice( videoIndex + 1 ) ],
				_meta: {
					...state._meta,
					items: {
						..._metaItems,
						[ id ]: {
							..._metaVideo,
							isDeleting: true,
						},
					},
				},
			};
		}

		/*
		 * DELETE_VIDEO is the action trigger
		 * right after the video is removed from the server,
		 */
		case DELETE_VIDEO: {
			const { id, hasBeenDeleted, video: deletedVideo } = action;
			const _metaItems = state?._meta?.items || [];
			const _metaVideo = _metaItems[ id ] || {};
			const uploadedVideoCount = state.uploadedVideoCount - 1;

			if ( ! _metaVideo ) {
				return state;
			}

			return {
				...state,
				uploadedVideoCount,
				_meta: {
					...state._meta,
					relyOnInitialState: false,
					items: {
						..._metaItems,
						[ id ]: {
							..._metaVideo,
							isDeleting: false,
							hasBeenDeleted,
							deletedVideo,
						},
					},
				},
			};
		}

		case SET_VIDEOS_STORAGE_USED: {
			return {
				...state,
				storageUsed: action.used,
			};
		}

		case SET_IS_FETCHING_UPLOADED_VIDEO_COUNT: {
			return {
				...state,
				isFetchingUploadedVideoCount: action.isFetchingUploadedVideoCount,
			};
		}

		case SET_UPLOADED_VIDEO_COUNT: {
			return {
				...state,
				uploadedVideoCount: action.uploadedVideoCount,
				isFetchingUploadedVideoCount: false,
			};
		}

		case UPLOADING_VIDEO: {
			const { id, title } = action;
			const currentMeta = state?._meta || {};
			const currentMetaItems = currentMeta?.items || {};

			return {
				...state,
				_meta: {
					...currentMeta,
					items: {
						...currentMetaItems,
						[ id ]: {
							title,
							uploading: true,
						},
					},
				},
			};
		}

		case PROCESSING_VIDEO: {
			const { id, data } = action;
			const query = state?.query ?? getDefaultQuery();
			const pagination = { ...state.pagination };

			const items = [ ...( state?.items ?? [] ) ];
			const currentMeta = state?._meta || {};
			const currentMetaItems = Object.assign( {}, currentMeta?.items || {} );
			const title = currentMetaItems[ id ]?.title || '';

			let total = state?.uploadedVideoCount ?? 0;

			// Not update total and pagination if user is searching or not in the first page.
			if ( query?.page === 1 && ! query?.search ) {
				// Updating pagination and count
				total = ( state?.uploadedVideoCount ?? 0 ) + 1;
				pagination.total = total;
				pagination.totalPages = Math.ceil( total / query?.itemsPerPage );

				// Insert new video
				items.unshift( {
					id: data.id,
					guid: data.guid,
					url: data.src,
					title,
					posterImage: null,
					finished: false,
				} );
			}

			// Remove video from uploading meta
			delete currentMetaItems[ id ];

			return {
				...state,
				items,
				uploadedVideoCount: total,
				pagination,
				_meta: {
					...currentMeta,
					items: currentMetaItems,
				},
			};
		}

		case UPLOADED_VIDEO: {
			const { video } = action;
			const items = [ ...( state?.items ?? [] ) ];
			const videoIndex = items.findIndex( item => item.id === video.id );

			// Probably user is searching or in another page than first
			if ( videoIndex === -1 ) {
				return state;
			}

			items[ videoIndex ] = video;

			return {
				...state,
				items,
			};
		}

		case SET_UPDATING_VIDEO_POSTER: {
			const { id } = action;
			const currentMeta = state?._meta || {};
			const currentMetaItems = currentMeta?.items || {};
			const currentVideoMeta = currentMetaItems[ id ] || {};

			return {
				...state,
				_meta: {
					...currentMeta,
					items: {
						...currentMetaItems,
						[ id ]: {
							...currentVideoMeta,
							isUpdatingPoster: true,
						},
					},
				},
			};
		}

		case UPDATE_VIDEO_POSTER: {
			const { id, poster } = action;
			const items = [ ...( state.items ?? [] ) ];
			const currentMeta = state?._meta || {};
			const currentMetaItems = currentMeta?.items || {};
			const videoIndex = items.findIndex( item => item.id === id );

			if ( videoIndex >= 0 ) {
				items[ videoIndex ] = {
					...items[ videoIndex ],
					posterImage: poster,
				};
			}

			return {
				...state,
				items,
				_meta: {
					...currentMeta,
					items: {
						...currentMetaItems,
						[ id ]: {
							isUpdatingPoster: false,
						},
					},
				},
			};
		}

		default:
			return state;
	}
};

const localVideos = ( state, action ) => {
	switch ( action.type ) {
		case SET_LOCAL_VIDEOS: {
			const { videos: items } = action;
			return {
				...state,
				items,
				isFetching: false,
			};
		}

		case SET_IS_FETCHING_LOCAL_VIDEOS: {
			return {
				...state,
				isFetching: action.isFetching,
			};
		}

		case SET_LOCAL_VIDEOS_QUERY:
			return {
				...state,
				query: {
					...state.query,
					...action.query,
				},
				_meta: {
					...state._meta,
					relyOnInitialState: false,
				},
			};

		case SET_LOCAL_VIDEOS_PAGINATION: {
			return {
				...state,
				pagination: {
					...state.pagination,
					...action.pagination,
				},
				_meta: {
					...state._meta,
					relyOnInitialState: false,
				},
			};
		}

		case SET_LOCAL_VIDEO_UPLOADED: {
			const { id } = action;
			const items = [ ...( state?.items ?? [] ) ];
			const index = items.findIndex( item => item.id === id );

			if ( index === -1 ) {
				return state;
			}

			items[ index ] = {
				...items[ index ],
				isUploadedToVideoPress: true,
			};

			return {
				...state,
				items,
				isFetching: false,
			};
		}
	}

	return state;
};

const users = ( state, action ) => {
	switch ( action.type ) {
		case SET_USERS: {
			return {
				...state,
				items: action.users,
			};
		}

		case SET_USERS_PAGINATION: {
			return {
				...state,
				pagination: {
					...( state?.pagination || {} ),
					...action.pagination,
				},
			};
		}

		default:
			return state;
	}
};

const purchases = ( state, action ) => {
	switch ( action.type ) {
		case SET_IS_FETCHING_PURCHASES: {
			return {
				...state,
				isFetching: action.isFetching,
			};
		}

		case SET_PURCHASES: {
			return {
				...state,
				items: action.purchases,
				isFetching: false,
			};
		}

		default:
			return state;
	}
};

const playbackTokens = ( state, action ) => {
	switch ( action.type ) {
		case SET_IS_FETCHING_PLAYBACK_TOKEN: {
			return {
				...state,
				isFetching: action.isFetching,
			};
		}

		case SET_PLAYBACK_TOKEN: {
			const { playbackToken } = action;
			const items = [ ...( state.items ?? [] ) ];
			const playbackTokenIndex = items.findIndex( item => item.guid === playbackToken.guid );

			if ( playbackTokenIndex === -1 ) {
				// Add it to the array
				items.unshift( playbackToken );
			} else {
				// Update it
				items[ playbackTokenIndex ] = {
					...items[ playbackTokenIndex ],
					...playbackToken,
				};
			}

			return {
				...state,
				items,
				isFetching: false,
			};
		}

		default:
			return state;
	}
};

const reducers = combineReducers( {
	videos,
	localVideos,
	purchases,
	users,
	playbackTokens,
} );

export default reducers;
