// 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 {intlShape} from 'react-intl';
import {Platform, View, Text} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import {Navigation} from 'react-native-navigation';

import {debounce} from 'mattermost-redux/actions/helpers';
import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';

import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import BottomSheet from 'app/utils/bottom_sheet';
import CustomList from 'app/components/custom_list';
import ChannelListRow from 'app/components/custom_list/channel_list_row';
import FormattedText from 'app/components/formatted_text';
import KeyboardLayout from 'app/components/layout/keyboard_layout';
import Loading from 'app/components/loading';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {NavigationTypes} from 'app/constants';
import {alertErrorWithFallback, emptyFunction} from 'app/utils/general';
import {goToScreen, dismissModal, setButtons} from 'app/actions/navigation';
import {
    changeOpacity,
    makeStyleSheetFromTheme,
    setNavigatorStyles,
    getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';

export default class MoreChannels extends PureComponent {
    static propTypes = {
        actions: PropTypes.shape({
            getArchivedChannels: PropTypes.func.isRequired,
            getChannels: PropTypes.func.isRequired,
            handleSelectChannel: PropTypes.func.isRequired,
            joinChannel: PropTypes.func.isRequired,
            searchChannels: PropTypes.func.isRequired,
            setChannelDisplayName: PropTypes.func.isRequired,
        }).isRequired,
        componentId: PropTypes.string,
        canCreateChannels: PropTypes.bool.isRequired,
        channels: PropTypes.array,
        archivedChannels: PropTypes.array,
        closeButton: PropTypes.object,
        currentUserId: PropTypes.string.isRequired,
        currentTeamId: PropTypes.string.isRequired,
        theme: PropTypes.object.isRequired,
        isLandscape: PropTypes.bool.isRequired,
        canShowArchivedChannels: PropTypes.bool.isRequired,
    };

    static defaultProps = {
        channels: [],
    };

    static contextTypes = {
        intl: intlShape.isRequired,
    };

    constructor(props, context) {
        super(props, context);

        this.searchTimeoutId = 0;
        this.publicPage = -1;
        this.archivedPage = -1;
        this.nextPublic = true;
        this.nextArchived = true;
        this.mounted = false;

        this.state = {
            channels: props.channels.slice(0, General.CHANNELS_CHUNK_SIZE),
            archivedChannels: props.archivedChannels.slice(0, General.CHANNELS_CHUNK_SIZE),
            typeOfChannels: 'public',
            loading: false,
            adding: false,
            term: '',
        };

        this.rightButton = {
            color: props.theme.sidebarHeaderTextColor,
            id: 'create-pub-channel',
            text: context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}),
            showAsAction: 'always',
        };

        this.leftButton = {
            id: 'close-more-channels',
            icon: props.closeButton,
        };
    }

    componentDidMount() {
        this.navigationEventListener = Navigation.events().bindComponent(this);
        this.mounted = true;
        this.setHeaderButtons(this.props.canCreateChannels);
        this.doGetChannels();
    }

    componentWillUnmount() {
        this.mounted = false;
    }

    componentWillReceiveProps(nextProps) {
        const {term} = this.state;
        let channels;
        let archivedChannels;

        if (this.props.theme !== nextProps.theme) {
            setNavigatorStyles(this.props.componentId, nextProps.theme);
        }

        if (nextProps.channels !== this.props.channels) {
            channels = nextProps.channels;
            if (term) {
                channels = this.filterChannels(nextProps.channels, term);
            }
        }

        if (nextProps.archivedChannels !== this.props.archivedChannels) {
            archivedChannels = nextProps.archivedChannels;
            if (term) {
                archivedChannels = this.filterChannels(nextProps.archivedChannels, term);
            }
        }

        const nextState = {};
        if (channels) {
            nextState.channels = channels;
        }

        if (archivedChannels) {
            nextState.archivedChannels = archivedChannels;
        }

        if (nextState.archivedChannels || nextState.channels) {
            this.setState(nextState);
        }
    }

    navigationButtonPressed({buttonId}) {
        switch (buttonId) {
        case 'close-more-channels':
            this.close();
            break;
        case 'create-pub-channel':
            this.onCreateChannel();
            break;
        }
    }

    setSearchBarRef = (ref) => {
        this.searchBarRef = ref;
    }

    cancelSearch = () => {
        const {channels, archivedChannels} = this.props;

        this.setState({
            channels,
            archivedChannels,
            term: '',
        });
    };

    close = () => {
        dismissModal();
    };

    doGetChannels = () => {
        const {actions, currentTeamId, canShowArchivedChannels} = this.props;
        const {loading, term, typeOfChannels} = this.state;

        if (!loading && !term && this.mounted) {
            this.setState({loading: true}, () => {
                switch (typeOfChannels) {
                case 'public':
                    if (this.nextPublic) {
                        actions.getChannels(
                            currentTeamId,
                            this.publicPage + 1,
                            General.CHANNELS_CHUNK_SIZE,
                        ).then(this.loadedChannels);
                    }
                    break;
                case 'archived':
                    if (canShowArchivedChannels && this.nextArchived) {
                        actions.getArchivedChannels(
                            currentTeamId,
                            this.archivedPage + 1,
                            General.CHANNELS_CHUNK_SIZE,
                        ).then(this.loadedChannels);
                    }
                    break;
                }
            });
        }
    };

    filterChannels = (channels, term) => {
        const lowerCasedTerm = term.toLowerCase();
        return channels.filter((c) => {
            return (c.name.toLowerCase().includes(lowerCasedTerm) || c.display_name.toLowerCase().includes(lowerCasedTerm));
        });
    };

    getChannels = debounce(this.doGetChannels, 100);

    setHeaderButtons = (createEnabled) => {
        const {canCreateChannels, componentId} = this.props;
        const buttons = {
            leftButtons: [this.leftButton],
        };

        if (canCreateChannels) {
            buttons.rightButtons = [{...this.rightButton, enabled: createEnabled}];
        }

        setButtons(componentId, buttons);
    };

    loadedChannels = ({data}) => {
        if (this.mounted) {
            const {typeOfChannels} = this.state;
            const isPublic = typeOfChannels === 'public';

            if (isPublic) {
                this.publicPage += 1;
                this.nextPublic = (data && !data.length);
            } else {
                this.archivedPage += 1;
                this.nextArchived = (data && !data.length);
            }

            this.setState({loading: false});
        }
    };

    onSelectChannel = async (id) => {
        const {intl} = this.context;
        const {actions, currentTeamId, currentUserId} = this.props;
        const {channels} = this.state;

        this.setHeaderButtons(false);
        this.setState({adding: true});

        const channel = channels.find((c) => c.id === id);
        const result = await actions.joinChannel(currentUserId, currentTeamId, id);

        if (result.error) {
            alertErrorWithFallback(
                intl,
                result.error,
                {
                    id: 'mobile.join_channel.error',
                    defaultMessage: "We couldn't join the channel {displayName}. Please check your connection and try again.",
                },
                {
                    displayName: channel ? channel.display_name : '',
                },
            );
            this.setHeaderButtons(true);
            this.setState({adding: false});
        } else {
            if (channel) {
                actions.setChannelDisplayName(channel.display_name);
            } else {
                actions.setChannelDisplayName('');
            }
            await actions.handleSelectChannel(id);

            EventEmitter.emit(NavigationTypes.CLOSE_MAIN_SIDEBAR);
            requestAnimationFrame(() => {
                this.close();
            });
        }
    };

    onCreateChannel = () => {
        const {formatMessage} = this.context.intl;

        const screen = 'CreateChannel';
        const title = formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'});
        const passProps = {
            channelType: General.OPEN_CHANNEL,
        };

        goToScreen(screen, title, passProps);
    };

    renderLoading = () => {
        const {theme} = this.props;
        const {typeOfChannels} = this.state;
        const style = getStyleFromTheme(theme);

        if ((typeOfChannels === 'public' && !this.nextPublic) ||
            (typeOfChannels === 'archived' && !this.nextArchived)) {
            return null;
        }

        return (
            <View style={style.loadingContainer}>
                <FormattedText
                    id='mobile.loading_channels'
                    defaultMessage='Loading Channels...'
                    style={style.loadingText}
                />
            </View>
        );
    };

    renderNoResults = () => {
        const {term, loading} = this.state;
        const {theme} = this.props;
        const style = getStyleFromTheme(theme);

        if (loading) {
            return null;
        }

        if (term) {
            return (
                <View style={style.noResultContainer}>
                    <FormattedText
                        id='mobile.custom_list.no_results'
                        defaultMessage='No Results'
                        style={style.noResultText}
                    />
                </View>
            );
        }

        return (
            <View style={style.noResultContainer}>
                <FormattedText
                    id='more_channels.noMore'
                    defaultMessage='No more channels to join'
                    style={style.noResultText}
                />
            </View>
        );
    };

    renderItem = (props) => {
        return (
            <ChannelListRow
                {...props}
                isArchived={this.state.typeOfChannels === 'archived'}
            />
        );
    }

    searchChannels = (text) => {
        const {actions, channels, archivedChannels, currentTeamId, canShowArchivedChannels} = this.props;
        const {typeOfChannels} = this.state;

        if (text) {
            if (typeOfChannels === 'public') {
                const filtered = this.filterChannels(channels, text);
                this.setState({
                    channels: filtered,
                    term: text,
                });
                clearTimeout(this.searchTimeoutId);
            } else if (typeOfChannels === 'archived' && canShowArchivedChannels) {
                const filtered = this.filterChannels(archivedChannels, text);
                this.setState({
                    archivedChannels: filtered,
                    term: text,
                });
                clearTimeout(this.searchTimeoutId);
            }
            this.searchTimeoutId = setTimeout(() => {
                actions.searchChannels(currentTeamId, text.toLowerCase(), typeOfChannels === 'archived');
            }, General.SEARCH_TIMEOUT_MILLISECONDS);
        } else {
            this.cancelSearch();
        }
    };

    handleDropdownClick = () => {
        const {formatMessage} = this.context.intl;
        const publicChannelsText = formatMessage({id: 'more_channels.publicChannels', defaultMessage: 'Public Channels'});
        const archivedChannelsText = formatMessage({id: 'more_channels.archivedChannels', defaultMessage: 'Archived Channels'});
        const titleText = formatMessage({id: 'more_channels.dropdownTitle', defaultMessage: 'Show'});
        const cancelText = 'Cancel';
        BottomSheet.showBottomSheetWithOptions({
            options: [publicChannelsText, archivedChannelsText, cancelText],
            cancelButtonIndex: 2,
            title: titleText,
        }, (value) => {
            let typeOfChannels;
            switch (value) {
            case 0:
                typeOfChannels = 'public';
                break;
            case 1:
                typeOfChannels = 'archived';
                break;
            default:
                typeOfChannels = this.state.typeOfChannels;
            }

            if (typeOfChannels !== this.state.typeOfChannels) {
                this.setState({typeOfChannels, loading: false}, this.doGetChannels);
            }
        });
    }

    render() {
        const {formatMessage} = this.context.intl;
        const {theme, isLandscape, canShowArchivedChannels} = this.props;
        const {adding, channels, archivedChannels, loading, term, typeOfChannels} = this.state;
        const more = term ? emptyFunction : this.getChannels;
        const style = getStyleFromTheme(theme);

        const publicChannelsText = formatMessage({id: 'more_channels.showPublicChannels', defaultMessage: 'Show: Public Channels'});
        const archivedChannelsText = formatMessage({id: 'more_channels.showArchivedChannels', defaultMessage: 'Show: Archived Channels'});

        let content;
        if (adding) {
            content = (<Loading color={theme.centerChannelColor}/>);
        } else {
            const searchBarInput = {
                backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
                color: theme.centerChannelColor,
                fontSize: 15,
                ...Platform.select({
                    android: {
                        marginBottom: -5,
                    },
                }),
            };

            let activeChannels = channels;

            if (canShowArchivedChannels && typeOfChannels === 'archived') {
                activeChannels = archivedChannels;
            }

            let channelDropdown;
            if (canShowArchivedChannels) {
                channelDropdown = (
                    <View style={[style.titleContainer, padding(isLandscape)]}>
                        <Text
                            accessibilityRole={'button'}
                            style={style.channelDropdown}
                            onPress={this.handleDropdownClick}
                        >
                            {typeOfChannels === 'public' ? publicChannelsText : archivedChannelsText}
                            {'  '}
                            <Icon
                                name={'caret-down'}
                            />
                        </Text>
                    </View>
                );
            }

            content = (
                <React.Fragment>
                    <View style={[style.searchBar, padding(isLandscape)]}>
                        <SearchBar
                            ref={this.setSearchBarRef}
                            placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
                            cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
                            backgroundColor='transparent'
                            inputHeight={33}
                            inputStyle={searchBarInput}
                            placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.5)}
                            tintColorSearch={changeOpacity(theme.centerChannelColor, 0.5)}
                            tintColorDelete={changeOpacity(theme.centerChannelColor, 0.5)}
                            titleCancelColor={theme.centerChannelColor}
                            onChangeText={this.searchChannels}
                            onSearchButtonPress={this.searchChannels}
                            onCancelButtonPress={this.cancelSearch}
                            autoCapitalize='none'
                            keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
                            value={term}
                        />
                    </View>
                    {channelDropdown}
                    <CustomList
                        canRefresh={false}
                        data={activeChannels}
                        extraData={loading}
                        key='custom_list'
                        loading={loading}
                        loadingComponent={this.renderLoading()}
                        noResults={this.renderNoResults()}
                        onLoadMore={more}
                        onRowPress={this.onSelectChannel}
                        renderItem={this.renderItem}
                        theme={theme}
                    />
                </React.Fragment>
            );
        }

        return (
            <KeyboardLayout>
                <StatusBar/>
                {content}
            </KeyboardLayout>
        );
    }
}

const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
    return {
        searchBar: {
            marginVertical: 5,
        },
        loadingContainer: {
            alignItems: 'center',
            backgroundColor: theme.centerChannelBg,
            height: 70,
            justifyContent: 'center',
        },
        loadingText: {
            color: changeOpacity(theme.centerChannelColor, 0.6),
        },
        noResultContainer: {
            flexGrow: 1,
            flexDirection: 'row',
            alignItems: 'center',
            justifyContent: 'center',
        },
        noResultText: {
            fontSize: 26,
            color: changeOpacity(theme.centerChannelColor, 0.5),
        },
        channelDropdown: {
            color: theme.centerChannelColor,
            fontWeight: 'bold',
            marginLeft: 10,
            marginTop: 20,
            marginBottom: 10,
        },
    };
});
