// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
    Animated,
    InteractionManager,
    Keyboard,
    TouchableWithoutFeedback,
    StyleSheet,
    View,
    Platform,
} from 'react-native';
import {intlShape} from 'react-intl';

import {SearchBar} from 'react-native-elements';

import {memoizeResult} from '@mm-redux/utils/helpers';

import CompassIcon from '@components/compass_icon';

const LEFT_COMPONENT_INITIAL_POSITION = Platform.OS === 'ios' ? 7 : 0;

export default class Search extends PureComponent {
    static propTypes = {
        testID: PropTypes.string,
        onBlur: PropTypes.func,
        onFocus: PropTypes.func,
        onSearchButtonPress: PropTypes.func,
        onChangeText: PropTypes.func,
        onCancelButtonPress: PropTypes.func,
        onSelectionChange: PropTypes.func,
        backgroundColor: PropTypes.string,
        placeholderTextColor: PropTypes.string,
        titleCancelColor: PropTypes.string,
        tintColorSearch: PropTypes.string,
        tintColorDelete: PropTypes.string,
        selectionColor: PropTypes.string,
        inputStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
        containerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
        cancelButtonStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
        autoFocus: PropTypes.bool,
        placeholder: PropTypes.string,
        cancelTitle: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.object,
        ]),
        returnKeyType: PropTypes.string,
        keyboardType: PropTypes.string,
        autoCapitalize: PropTypes.string,
        inputHeight: PropTypes.number,
        editable: PropTypes.bool,
        blurOnSubmit: PropTypes.bool,
        keyboardShouldPersist: PropTypes.bool,
        value: PropTypes.string,
        keyboardAppearance: PropTypes.string,
        showArrow: PropTypes.bool,
        searchBarRightMargin: PropTypes.number,
        leftComponent: PropTypes.element,
        searchIconSize: PropTypes.number,
        backArrowSize: PropTypes.number,
        deleteIconSize: PropTypes.number,
        showCancel: PropTypes.bool,
        containerHeight: PropTypes.number,
    };

    static contextTypes = {
        intl: intlShape,
    };

    static defaultProps = {
        onSelectionChange: () => true,
        onBlur: () => true,
        editable: true,
        blurOnSubmit: false,
        keyboardShouldPersist: false,
        placeholderTextColor: 'grey',
        value: '',
        showArrow: false,
        showCancel: true,
        searchIconSize: 24,
        backArrowSize: 24,
        deleteIconSize: 20,
        searchBarRightMargin: 0,
        returnKeyType: 'search',
        keyboardType: 'default',
        containerHeight: 40,
    };

    constructor(props) {
        super(props);
        this.state = {
            leftComponentWidth: 0,
        };

        this.leftComponentAnimated = new Animated.Value(LEFT_COMPONENT_INITIAL_POSITION);
        this.searchContainerAnimated = new Animated.Value(0);
    }

    setSearchContainerRef = (ref) => {
        this.searchContainerRef = ref;
    }

    setInputKeywordRef = (ref) => {
        this.inputKeywordRef = ref;
    }

    blur = () => {
        this.inputKeywordRef.blur();
    };

    focus = () => {
        this.inputKeywordRef.focus();
    };

    onBlur = async () => {
        this.props.onBlur();

        if (this.props.leftComponent) {
            await this.collapseAnimation();
        }
    };

    onLeftComponentLayout = (event) => {
        const leftComponentWidth = event.nativeEvent.layout.width;
        this.setState({leftComponentWidth});
    };

    onSearch = async () => {
        if (this.props.keyboardShouldPersist === false) {
            await Keyboard.dismiss();
        }

        this.props.onSearchButtonPress(this.props.value);
    };

    onChangeText = (text) => {
        if (this.props.onChangeText) {
            this.props.onChangeText(text);
        }
    };

    onFocus = () => {
        InteractionManager.runAfterInteractions(async () => {
            if (this.props.onFocus) {
                this.props.onFocus();
            }

            if (this.props.leftComponent) {
                await this.expandAnimation();
            }
        });
    };

    onClear = () => {
        this.focus();
        this.props.onChangeText('', true);
    };

    onCancel = () => {
        Keyboard.dismiss();
        InteractionManager.runAfterInteractions(() => {
            if (this.props.onCancelButtonPress) {
                this.props.onCancelButtonPress();
            }
        });
    };

    onSelectionChange = (event) => {
        this.props.onSelectionChange(event);
    };

    expandAnimation = () => {
        return new Promise((resolve) => {
            Animated.parallel([
                Animated.timing(
                    this.leftComponentAnimated,
                    {
                        toValue: -115,
                        duration: 200,
                    },
                ),
                Animated.timing(
                    this.searchContainerAnimated,
                    {
                        toValue: this.state.leftComponentWidth * -1,
                        duration: 200,
                    },
                ),
            ]).start(resolve);
        });
    }

    collapseAnimation = () => {
        return new Promise((resolve) => {
            Animated.parallel([
                Animated.timing(
                    this.leftComponentAnimated,
                    {
                        toValue: LEFT_COMPONENT_INITIAL_POSITION,
                        duration: 200,
                    },
                ),
                Animated.timing(
                    this.searchContainerAnimated,
                    {
                        toValue: 0,
                        duration: 200,
                    },
                ),
            ]).start(resolve);
        });
    }

    render() {
        const {testID, backgroundColor, inputHeight, inputStyle, placeholderTextColor, tintColorSearch, cancelButtonStyle, tintColorDelete, titleCancelColor, searchBarRightMargin, containerHeight} = this.props;
        const searchClearButtonTestID = `${testID}.search.clear.button`;
        const searchCancelButtonTestID = `${testID}.search.cancel.button`;
        const searchInputTestID = `${testID}.search.input`;
        const searchBarStyle = getSearchBarStyle(
            backgroundColor,
            cancelButtonStyle,
            inputHeight,
            inputStyle,
            placeholderTextColor,
            tintColorDelete,
            tintColorSearch,
            titleCancelColor,
            searchBarRightMargin,
            containerHeight,
        );
        const {intl} = this.context;

        let clearIcon = null;
        let searchIcon = null;
        let cancelIcon = null;

        if (Platform.OS === 'ios') {
            clearIcon = (
                <CompassIcon
                    testID={searchClearButtonTestID}
                    name='close-circle'
                    size={18}
                    style={{color: searchBarStyle.clearIconColorIos}}
                    onPress={this.onClear}
                />
            );

            searchIcon = (
                <CompassIcon
                    name='magnify'
                    size={24}
                    style={[
                        styles.fullWidth,
                        searchBarStyle.searchIcon,
                    ]}
                />
            );
        } else {
            searchIcon = this.props.showArrow ?
                (
                    <TouchableWithoutFeedback onPress={this.onCancel}>
                        <CompassIcon
                            testID={searchCancelButtonTestID}
                            name='arrow-left'
                            size={this.props.backArrowSize}
                            color={searchBarStyle.clearIconColorAndroid}
                        />
                    </TouchableWithoutFeedback>
                ) :
                (
                    <CompassIcon
                        name='magnify'
                        size={this.props.searchIconSize}
                        color={searchBarStyle.searchIconColor}
                    />
                );

            // Making sure the icon won't change depending on whether the input is in focus on Android devices
            cancelIcon = (
                <CompassIcon
                    testID={searchCancelButtonTestID}
                    name='arrow-left'
                    size={25}
                    color={searchBarStyle.clearIconColorAndroid}
                    onPress={this.onCancel}
                />
            );

            clearIcon = (
                <CompassIcon
                    testID={searchClearButtonTestID}
                    name='close'
                    size={this.props.deleteIconSize}
                    color={searchBarStyle.clearIconColorAndroid}
                    onPress={this.onClear}
                />
            );
        }

        return (
            <View
                testID={testID}
                style={[searchBarStyle.container, this.props.containerStyle]}
            >
                {((this.props.leftComponent) ?
                    <Animated.View
                        style={[styles.leftComponent, {
                            left: this.leftComponentAnimated,
                        }]}
                        onLayout={this.onLeftComponentLayout}
                    >
                        {this.props.leftComponent}
                    </Animated.View> :
                    null
                )}
                <Animated.View
                    style={[
                        styles.fullWidth,
                        searchBarStyle.searchBarWrapper,
                        {
                            marginLeft: this.searchContainerAnimated,
                        },
                    ]}
                >
                    <SearchBar
                        testID={searchInputTestID}
                        autoCapitalize={this.props.autoCapitalize}
                        autoCorrect={false}
                        autoFocus={this.props.autoFocus}
                        blurOnSubmit={this.props.blurOnSubmit}
                        cancelButtonProps={{
                            buttonTextStyle: {
                                ...styles.text,
                                ...searchBarStyle.cancelButtonText,
                            },
                        }}
                        cancelButtonTitle={this.props.cancelTitle || intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
                        cancelIcon={cancelIcon}
                        clearIcon={clearIcon}
                        containerStyle={{
                            ...styles.searchContainer,
                            ...styles.fullWidth,
                            ...searchBarStyle.searchBarContainer,
                        }}
                        disableFullscreenUI={true}
                        editable={this.props.editable}
                        enablesReturnKeyAutomatically={true}
                        inputContainerStyle={{
                            ...styles.inputContainer,
                            ...searchBarStyle.inputContainer,
                        }}
                        inputStyle={{
                            ...styles.text,
                            ...styles.inputMargin,
                            ...searchBarStyle.inputStyle,
                        }}
                        keyboardAppearance={this.props.keyboardAppearance}
                        keyboardType={this.props.keyboardType}
                        leftIconContainerStyle={styles.leftIcon}
                        placeholder={this.props.placeholder || intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
                        placeholderTextColor={this.props.placeholderTextColor}
                        platform={Platform.OS}
                        onBlur={this.onBlur}
                        onCancel={this.onCancel}
                        onChangeText={this.onChangeText}
                        onClear={this.onClear}
                        onFocus={this.onFocus}
                        onSelectionChange={this.onSelectionChange}
                        onSubmitEditing={this.onSearch}
                        searchIcon={searchIcon}
                        selectionColor={this.props.selectionColor}
                        showCancel={this.props.showCancel}
                        ref={this.setInputKeywordRef}
                        returnKeyType={this.props.returnKeyType}
                        underlineColorAndroid='transparent'
                        value={this.props.value}
                    />
                </Animated.View>
            </View>
        );
    }
}

const getSearchBarStyle = memoizeResult((
    backgroundColor,
    cancelButtonStyle,
    inputHeight,
    inputStyle,
    placeholderTextColor,
    tintColorDelete,
    tintColorSearch,
    titleCancelColor,
    searchBarRightMargin,
    containerHeight,
) => ({
    cancelButtonText: {
        ...cancelButtonStyle,
        color: titleCancelColor,
    },
    container: {
        flexDirection: 'row',
        justifyContent: 'flex-start',
        alignItems: 'center',
        height: containerHeight,
        overflow: 'hidden',
    },
    clearIconColorIos: tintColorDelete || styles.defaultColor.color,
    clearIconColorAndroid: titleCancelColor || placeholderTextColor,
    inputStyle: {
        ...inputStyle,
        backgroundColor: 'transparent',
        height: inputHeight,
    },
    inputContainer: {
        backgroundColor: inputStyle.backgroundColor,
        height: inputHeight,
    },
    searchBarWrapper: {
        marginRight: searchBarRightMargin,
        height: Platform.select({
            ios: inputHeight || containerHeight - 10,
            android: inputHeight,
        }),
    },
    searchBarContainer: {
        backgroundColor,
    },
    searchIcon: {
        color: tintColorSearch || placeholderTextColor,
        top: 8,
    },
    searchIconColor: tintColorSearch || placeholderTextColor,
}));

const styles = StyleSheet.create({
    defaultColor: {
        color: 'grey',
    },
    fullWidth: {
        flex: 1,
    },
    inputContainer: {
        borderRadius: Platform.select({
            ios: 2,
            android: 0,
        }),
    },
    inputMargin: {
        marginLeft: 4,
        paddingTop: 0,
        marginTop: Platform.select({
            ios: 0,
            android: 8,
        }),
    },
    leftIcon: {
        marginLeft: 4,
        width: 30,
    },
    searchContainer: {
        paddingTop: 0,
        paddingBottom: 0,
    },
    text: {
        fontSize: Platform.select({
            ios: 14,
            android: 15,
        }),
        color: '#fff',
    },
    leftComponent: {
        position: 'relative',
        marginLeft: 2,
    },
});
