import findIndex from 'lodash/findIndex';
import find from 'lodash/find';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import isEqual from 'lodash/isEqual';
import { generateImageId, createSVGImageData } from './Modifiers';
import * as actionTypes from './Actions';
import { ImageGalleryModes } from './Actions';
import { isOneColorAboveThreshold } from '../DesignData/Modifiers';
import { isHardModified } from './Selectors';
import { getConf } from '../../configuration';

const updateDerivedImageDataProps = imageData => {
    const { operations = {} } = imageData;

    imageData.imageColorsWereEdited =
        !!operations.vectorizeOriginalColorsMap &&
        !isEqual(operations.vectorizeColorsMap, operations.vectorizeOriginalColorsMap);

    const prevColorPreviewEnabled = imageData.colorPreviewEnabled;

    imageData.colorPreviewEnabled =
        imageData.colorPreviewRequired ||
        (!!Object.keys(operations.vectorizeOriginalColorsMap || {}).length &&
            isOneColorAboveThreshold(operations.vectorizeColorsMap, operations.vectorizeOriginalColorsMap));

    if (!imageData.colorPreviewEnabled) {
        imageData.displayColorPreview = false;
    } else if (!prevColorPreviewEnabled) {
        imageData.displayColorPreview = getConf('autoPreviewVectorization', true);
    }

    if (imageData.vectorizedLogoMandatory) {
        imageData.colorPreviewEnabled = false;
        imageData.displayColorPreview = true;
    }

    return imageData;
};

const initialState = {
    gallery: [],
    tags: [],
    selectedTag: null,
    selectedTags: [],
    userImages: [],
    selectedImage: null,
    lastErrorCode: null,
    mode: null,
    uploadDelayed: false,
};

export function imageGalleryReducer(state = initialState, action = {}) {
    switch (action.type) {
        case actionTypes.RECEIVE_GALLERY:
            return {
                ...state,
                gallery: action.data,
            };
        case actionTypes.RECEIVE_GALLERY_TAGS:
            return {
                ...state,
                tags: action.data,
            };
        case actionTypes.SELECT_GALLERY_TAG:
            return {
                ...state,
                selectedTag: action.tag,
            };
        case actionTypes.SELECT_GALLERY_TAGS: {
            return {
                ...state,
                selectedTags: action.tags,
            };
        }
        case actionTypes.RECEIVE_IMAGE_UPLOAD_DATA: {
            const statusCode = get(action, 'response.status');
            if (statusCode && statusCode !== 200) {
                return {
                    ...state,
                    lastErrorCode: get(action, 'data.code', 'unknown_server_error'),
                };
            }
            const imageData = { ...action.imageData, ...action.data };

            let userImages = state.userImages;
            const userImage = find(userImages, { identifier: imageData.identifier }) || state.selectedImage;

            if (userImage) {
                imageData.identifier = userImage.identifier;
                if (userImage.originalFileName === imageData.originalFileName) {
                    imageData.displayColorPreview = userImage.displayColorPreview;

                    if (!imageData.operations) {
                        imageData.operations = cloneDeep(userImage.operations);
                    }
                }
            }

            if (!imageData.identifier || action.createCopy) {
                imageData.identifier = generateImageId();
            }

            updateDerivedImageDataProps(imageData);

            if (action.createCopy && action.immediateCommit) {
                userImages = [].concat(state.userImages);
                userImages.push(imageData);
            }

            return {
                ...state,
                userImages,
                selectedImage: imageData,
                lastErrorCode: null, // clear the error code if succeeded
            };
        }
        case actionTypes.COMMIT_CHANGES: {
            const {
                imageUsageInfo: { count },
                compatibilityInfoHash,
            } = action;
            const { selectedImage, mode } = state;
            const userImages = [].concat(state.userImages);

            const { identifier, fileName, operations } = selectedImage;

            const currentImage =
                mode === ImageGalleryModes.add
                    ? find(userImages, { fileName, operations, compatibilityInfoHash }) // try to find an existing one with the same options
                    : count === 1 // if it's the only image, find it by identifier
                    ? find(userImages, { identifier })
                    : find(userImages, { identifier, operations, compatibilityInfoHash }) || null;

            if (currentImage) {
                if (mode === ImageGalleryModes.add) {
                    return {
                        ...state,
                        selectedImage: currentImage,
                    };
                } else {
                    const idx = userImages.indexOf(currentImage);
                    userImages[idx] = {
                        ...currentImage,
                        ...selectedImage,
                    };
                    return {
                        ...state,
                        userImages,
                    };
                }
            }
            const newImage = {
                ...selectedImage,
                compatibilityInfoHash,
                operations: operations || {},
                identifier: generateImageId(),
            };
            userImages.push(newImage);
            return {
                ...state,
                userImages,
                selectedImage: newImage,
            };
        }
        case actionTypes.SET_MODE: {
            const { mode } = action;
            return {
                ...state,
                mode,
            };
        }
        case actionTypes.SET_SELECTED_IMAGE: {
            const { imageData, fileName, identifier } = action;
            const selectedImage =
                (imageData && updateDerivedImageDataProps(imageData)) ||
                (identifier && find(state.userImages, { identifier })) ||
                (fileName && find(state.userImages, { fileName })) ||
                null;

            return {
                ...state,
                selectedImage,
            };
        }
        case actionTypes.ADD_IMAGES_FROM_DESIGN_AREA: {
            return action.data
                ? {
                      ...state,
                      userImages: [...state.userImages, ...action.data],
                  }
                : state;
        }
        case actionTypes.UPDATE_IMAGE: {
            const { userImages } = state;

            const {
                imageUsageInfo: { designAreas },
                imageDataPatch,
            } = action;

            const currentImage =
                state.selectedImage && state.selectedImage.identifier === action.identifier
                    ? state.selectedImage
                    : find(userImages, { identifier: action.identifier });

            const image = {
                ...currentImage,
                ...imageDataPatch,
            };

            // create new identifier if it's used by multiple areas
            // avoiding unwanted modification of other occurrences
            if (designAreas > 1 && isHardModified(currentImage, image)) {
                image.identifier = generateImageId();
            }

            updateDerivedImageDataProps(image);

            const result = {
                ...state,
            };

            result.selectedImage = image;

            // in 'immediate commit' mode the image is directly updated or added
            // so the extra COMMIT_CHANGES action does not have to be performed
            if (action.immediateCommit) {
                result.userImages = [...userImages];

                const idx = findIndex(userImages, { identifier: image.identifier });
                if (idx !== -1) {
                    result.userImages[idx] = image;
                } else {
                    result.userImages.push(image);
                }
            }

            return result;
        }
        case actionTypes.ADD_GALLERY_IMAGE_TO_USER_IMAGES: {
            const {
                imageUrl,
                thumbUrl,
                fileName,
                vectorizeColorsMap,
                compatibilityInfoHash,
                svgImageSize,
            } = action;

            const image = createSVGImageData({
                imageUrl,
                thumbUrl,
                fileName,
                svgImageSize,
                operations: {
                    vectorizeColorsMap,
                },
            });

            return {
                ...state,
                selectedImage: {
                    ...image,
                    compatibilityInfoHash,
                },
            };
        }
        case actionTypes.DISMISS_ERROR: {
            return {
                ...state,
                lastErrorCode: null,
            };
        }
        case actionTypes.UPDATE_UPLOAD_DELAYED: {
            return {
                ...state,
                uploadDelayed: action.uploadDelayed,
            };
        }
        default:
            return state;
    }
}

export default imageGalleryReducer;
