// @flow

import * as React from 'react';
import {Platform} from "react-native";
import i18n from "i18n-js";
import {Searchbar} from "react-native-paper";
import {stringMatchQuery} from "../../utils/Search";
import WebSectionList from "../../components/Screens/WebSectionList";
import GroupListAccordion from "../../components/Lists/PlanexGroups/GroupListAccordion";
import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {StackNavigationProp} from "@react-navigation/stack";

const LIST_ITEM_HEIGHT = 70;

export type group = {
    name: string,
    id: number,
    isFav: boolean,
};

export type groupCategory = {
    name: string,
    id: number,
    content: Array<group>,
};

type Props = {
    navigation: StackNavigationProp,
}

type State = {
    currentSearchString: string,
    favoriteGroups: Array<group>,
};

function sortName(a: group | groupCategory, b: group | groupCategory) {
    if (a.name.toLowerCase() < b.name.toLowerCase())
        return -1;
    if (a.name.toLowerCase() > b.name.toLowerCase())
        return 1;
    return 0;
}

const GROUPS_URL = 'http://planex.insa-toulouse.fr/wsAdeGrp.php?projectId=1';
const REPLACE_REGEX = /_/g;

/**
 * Class defining planex group selection screen.
 */
class GroupSelectionScreen extends React.Component<Props, State> {

    constructor(props: Props) {
        super(props);
        this.state = {
            currentSearchString: '',
            favoriteGroups: JSON.parse(AsyncStorageManager.getInstance().preferences.planexFavoriteGroups.current),
        };
    }

    /**
     * Creates the header content
     */
    componentDidMount() {
        this.props.navigation.setOptions({
            headerTitle: this.getSearchBar,
            headerBackTitleVisible: false,
            headerTitleContainerStyle: Platform.OS === 'ios' ?
                {marginHorizontal: 0, width: '70%'} :
                {marginHorizontal: 0, right: 50, left: 50},
        });
    }

    /**
     * Gets the header search bar
     *
     * @return {*}
     */
    getSearchBar = () => {
        return (
            <Searchbar
                placeholder={i18n.t('screens.proximo.search')}
                onChangeText={this.onSearchStringChange}
            />
        );
    };

    /**
     * Callback used when the search changes
     *
     * @param str The new search string
     */
    onSearchStringChange = (str: string) => {
        this.setState({currentSearchString: str})
    };

    /**
     * Callback used when clicking an article in the list.
     * It opens the modal to show detailed information about the article
     *
     * @param item The article pressed
     */
    onListItemPress = (item: group) => {
        this.props.navigation.navigate("planex", {
            screen: "index",
            params: {group: item}
        });
    };

    /**
     * Callback used when the user clicks on the favorite button
     *
     * @param item The item to add/remove from favorites
     */
    onListFavoritePress = (item: group) => {
        this.updateGroupFavorites(item);
    };

    /**
     * Checks if the given group is in the favorites list
     *
     * @param group The group to check
     * @returns {boolean}
     */
    isGroupInFavorites(group: group) {
        let isFav = false;
        for (let i = 0; i < this.state.favoriteGroups.length; i++) {
            if (group.id === this.state.favoriteGroups[i].id) {
                isFav = true;
                break;
            }
        }
        return isFav;
    }

    /**
     * Removes the given group from the given array
     *
     * @param favorites The array containing favorites groups
     * @param group The group to remove from the array
     */
    removeGroupFromFavorites(favorites: Array<group>, group: group) {
        for (let i = 0; i < favorites.length; i++) {
            if (group.id === favorites[i].id) {
                favorites.splice(i, 1);
                break;
            }
        }
    }

    /**
     * Adds the given group to the given array
     *
     * @param favorites The array containing favorites groups
     * @param group The group to add to the array
     */
    addGroupToFavorites(favorites: Array<group>, group: group) {
        group.isFav = true;
        favorites.push(group);
        favorites.sort(sortName);
    }

    /**
     * Adds or removes the given group to the favorites list, depending on whether it is already in it or not.
     * Favorites are then saved in user preferences
     *
     * @param group The group to add/remove to favorites
     */
    updateGroupFavorites(group: group) {
        let newFavorites = [...this.state.favoriteGroups]
        if (this.isGroupInFavorites(group))
            this.removeGroupFromFavorites(newFavorites, group);
        else
            this.addGroupToFavorites(newFavorites, group);
        this.setState({favoriteGroups: newFavorites})
        AsyncStorageManager.getInstance().savePref(
            AsyncStorageManager.getInstance().preferences.planexFavoriteGroups.key,
            JSON.stringify(newFavorites));
    }

    /**
     * Checks whether to display the given group category, depending on user search query
     *
     * @param item The group category
     * @returns {boolean}
     */
    shouldDisplayAccordion(item: groupCategory) {
        let shouldDisplay = false;
        for (let i = 0; i < item.content.length; i++) {
            if (stringMatchQuery(item.content[i].name, this.state.currentSearchString)) {
                shouldDisplay = true;
                break;
            }
        }
        return shouldDisplay;
    }

    /**
     * Gets a render item for the given article
     *
     * @param item The article to render
     * @return {*}
     */
    renderItem = ({item}: { item: groupCategory }) => {
        if (this.shouldDisplayAccordion(item)) {
            return (
                <GroupListAccordion
                    item={item}
                    onGroupPress={this.onListItemPress}
                    onFavoritePress={this.onListFavoritePress}
                    currentSearchString={this.state.currentSearchString}
                    favoriteNumber={this.state.favoriteGroups.length}
                    height={LIST_ITEM_HEIGHT}
                />
            );
        } else
            return null;
    };

    /**
     * Generates the dataset to be used in the FlatList.
     * This improves formatting of group names, sorts alphabetically the categories, and adds favorites at the top.
     *
     * @param fetchedData The raw data fetched from the server
     * @returns {[]}
     */
    generateData(fetchedData: { [key: string]: groupCategory }) {
        let data = [];
        for (let key in fetchedData) {
            this.formatGroups(fetchedData[key]);
            data.push(fetchedData[key]);
        }
        data.sort(sortName);
        data.unshift({name: i18n.t("screens.planex.favorites"), id: 0, content: this.state.favoriteGroups});
        return data;
    }

    /**
     * Replaces underscore by spaces and sets the favorite state of every group in the given category
     *
     * @param item The category containing groups to format
     */
    formatGroups(item: groupCategory) {
        for (let i = 0; i < item.content.length; i++) {
            item.content[i].name = item.content[i].name.replace(REPLACE_REGEX, " ")
            item.content[i].isFav = this.isGroupInFavorites(item.content[i]);
        }
    }

    /**
     * Creates the dataset to be used in the FlatList
     *
     * @param fetchedData
     * @return {*}
     * */
    createDataset = (fetchedData: { [key: string]: groupCategory }) => {
        return [
            {
                title: '',
                data: this.generateData(fetchedData)
            }
        ];
    }

    render() {
        return (
            <WebSectionList
                {...this.props}
                createDataset={this.createDataset}
                autoRefreshTime={0}
                refreshOnFocus={false}
                fetchUrl={GROUPS_URL}
                renderItem={this.renderItem}
                updateData={this.state.currentSearchString + this.state.favoriteGroups.length}
                itemHeight={LIST_ITEM_HEIGHT}
            />
        );
    }
}

export default GroupSelectionScreen;
