import * as React from "react";
import * as _ from "lodash";
import * as DefaultStyles from "./style";
import { CheckItem } from "../../types/global";
import HierarchyList, { HierarchyList as HL } from "../HierarchyList";
import ListBar from "./ListBar";
import Search from "./Search";

export type ListComponent = React.ComponentType<HL.Props>;
export type ListFunc = (props: HL.Props) => JSX.Element;
export type List = ListComponent | ListFunc;

export const PickerContext = React.createContext({});

export namespace ListPicker {
    export interface Props {
        items: CheckItem[];
        checkedItems?: CheckItem[];
        styles?: ListPicker.Styles;
        listComponent?: List;
        listChange: (list: CheckItem[]) => void;
        id?: string;
        name?: string;
    }
    export interface State {
        checkedItems: CheckItem[];
        flatItems: CheckItem[];
        displayItems: CheckItem[];
        searchTerm: string;
    }
    export interface Styles {
        Container?: any;
        Median?: any;
        ListWrapper?: any;
        SearchInput?: any;
        SearchContainer?: any;
        ResetIcon?: any;
    }
}

class ListPicker extends React.Component<ListPicker.Props, ListPicker.State> {
    private Styles: ListPicker.Styles;
    private List: List;

    constructor(props: ListPicker.Props) {
        super(props);
        this.state = {
            checkedItems: props.checkedItems ? props.checkedItems : [],
            flatItems: this.flattenItems(props.items),
            displayItems: _.cloneDeep(props.items),
            searchTerm: "",
        };
        this.List = this.props.listComponent ? this.props.listComponent : HierarchyList;
    }

    toggleItem = (item: CheckItem): void => {
        const cloned = _.cloneDeep(item);
        cloned.children = [];
        let checkedItems: CheckItem[] = [];
        this.isSelected(cloned)
            ? (checkedItems = this.state.checkedItems.filter(
                i => i.id !== cloned.id
            ))
            : (checkedItems = _.concat(this.state.checkedItems, [cloned]));

        this.setState({ checkedItems });
        this.props.listChange(checkedItems);
    };

    flattenItems = (list: CheckItem[]): CheckItem[] =>
        _.flattenDeep(
            list.reduce((accum: any, item: CheckItem) => {
                accum.push([item, this.flattenItems(item.children)]);
                return accum;
            }, [])
        );

    reset = () => {
        this.setState({
            searchTerm: "",
            displayItems: _.cloneDeep(this.props.items),
            checkedItems: [],
        });
        this.props.listChange([]);
    };

    handleSearch = value => {
        let displayItems = this.state.flatItems.filter(item =>
            item.name.toLowerCase().includes(value.toLowerCase())
        );
        value === "" ? (displayItems = _.cloneDeep(this.props.items)) : null;
        this.setState({ searchTerm: value, displayItems });
    };

    isSelected = (item: CheckItem): boolean =>
        Boolean(this.state.checkedItems.find(i => i.id === item.id));

    configureLayout = () => {
        const childArr = React.Children.toArray(this.props.children);
        const searchChild = childArr.find((c: any) => c.type === Search);
        const listBarChild = childArr.find((c: any) => c.type === ListBar);
        if (searchChild && listBarChild) {
            return React.Children.map(this.props.children, (child: any) => {
                if (child.type === ListBar) {
                    return React.cloneElement(child, {
                        toggleItem: this.toggleItem,
                        displayItems: this.state.displayItems,
                        checkedItems: this.state.checkedItems,
                    });
                }
                if (child.type === Search) {
                    return React.cloneElement(child, {
                        value: this.state.searchTerm,
                        onChange: this.handleSearch,
                        onReset: this.reset,
                    });
                }
                return child;
            });
        } else if (!searchChild && listBarChild) {
            return (
                <>
                    <Search
                        value={this.state.searchTerm}
                        onChange={this.handleSearch}
                        onReset={this.reset}
                        id={this.props.id && this.props.id}
                        name={this.props.name && this.props.name}
                    />

                    {React.Children.map(this.props.children, (child: any) => {
                        if (child.type === ListBar) {
                            return React.cloneElement(child, {
                                toggleItem: this.toggleItem,
                                displayItems: this.state.displayItems,
                                checkedItems: this.state.checkedItems,
                            });
                        }
                        return null;
                    })}
                </>
            );
        } else {
            const { Styles, List } = this;
            return (
                <>
                    <Search
                        value={this.state.searchTerm}
                        onChange={this.handleSearch}
                        onReset={this.reset}
                        id={this.props.id && this.props.id}
                        name={this.props.name && this.props.name}
                    />
                    <Styles.ListWrapper id={this.props.id && this.props.id + '-list-picker-list-wrapper'} name={this.props.name && this.props.name + '-list-picker-list-wrapper'}>
                        <List
                            checkClicked={this.toggleItem}
                            items={this.state.displayItems}
                            checkedItems={this.state.checkedItems}
                        />

                        <Styles.Median id={this.props.id && this.props.id + '-list-picker-median-span'} name={this.props.name && this.props.name + '-list-picker-median-span'} />

                        <List
                            checkClicked={this.toggleItem}
                            items={this.state.checkedItems}
                            checkedItems={this.state.checkedItems}
                            keyPrefix={"selected"}
                        />
                    </Styles.ListWrapper>
                </>
            );
        }
    };

    render() {
        const Styles: ListPicker.Styles = _.merge({}, this.props.styles);
        return <Styles.Container id={this.props.id && this.props.id + '-list-picker-container'} name={this.props.name && this.props.name + '-list-picker-container'}>
            {this.configureLayout()}
        </Styles.Container>;
    }
}

export default ListPicker;
