import React = require('react');
import { action, computed, observable, ObservableMap } from 'mobx';
import { observer } from 'mobx-react';
import Select from 'react-select';
import { Doc, DocListCast, Field, StrListCast } from '../../fields/Doc';
import { RichTextField } from '../../fields/RichTextField';
import { StrCast } from '../../fields/Types';
import { DocumentManager } from '../util/DocumentManager';
import { UserOptions } from '../util/GroupManager';
import './FilterPanel.scss';
import { FieldView } from './nodes/FieldView';
import { SearchBox } from './search/SearchBox';
import { undoable } from '../util/UndoManager';
import { AiOutlineMinusSquare } from 'react-icons/ai';
import { CiCircleRemove } from 'react-icons/ci';

interface filterProps {
    rootDoc: Doc;
}

@observer
export class FilterPanel extends React.Component<filterProps> {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(FilterPanel, fieldKey);
    }

    /**
     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
     */
    @computed get targetDoc() {
        return this.props.rootDoc;
    }
    @computed get targetDocChildKey() {
        const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc);
        return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data';
    }
    @computed get targetDocChildren() {
        return [...DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.targetDoc[Doc.LayoutFieldKey(this.targetDoc) + '_sidebar'])];
    }

    @computed get allDocs() {
        const allDocs = new Set<Doc>();
        const targetDoc = this.targetDoc;
        if (targetDoc) {
            SearchBox.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc));
        }
        return Array.from(allDocs);
    }

    @computed get _allFacets() {
        // trace();
        const noviceReqFields = ['author', 'tags', 'text', 'type'];
        const noviceLayoutFields: string[] = []; //["_layout_curPage"];
        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];

        const keys = new Set<string>(noviceFields);
        this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
        const sortedKeys = Array.from(keys.keys())
            .filter(key => key[0])
            .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
            .sort();
        noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
        return [...noviceFields, ...sortedKeys];
    }

    /**
     * The current attributes selected to filter based on
     */
    @computed get activeFilters() {
        return StrListCast(this.targetDoc?._childFilters);
    }

    /**
     * @returns a string array of the current attributes
     */
    @computed get currentFacets() {
        return this.activeFilters.map(filter => filter.split(Doc.FilterSep)[0]);
    }

    gatherFieldValues(childDocs: Doc[], facetKey: string) {
        const valueSet = new Set<string>();
        let rtFields = 0;
        let subDocs = childDocs;
        if (subDocs.length > 0) {
            let newarray: Doc[] = [];
            while (subDocs.length > 0) {
                newarray = [];
                subDocs.forEach(t => {
                    const facetVal = t[facetKey];
                    if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
                    facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
                    (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal));
                    const fieldKey = Doc.LayoutFieldKey(t);
                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
                    DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                    annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
                });
                subDocs = newarray;
            }
        }
        //     }
        // });
        return { strings: Array.from(valueSet.keys()), rtFields };
    }

    public removeFilter = (filterName: string) => {
        Doc.setDocFilter(this.targetDoc, filterName, undefined, 'remove');
        Doc.setDocRangeFilter(this.targetDoc, filterName, undefined);
    };

    @observable _chosenFacets = new ObservableMap<string, 'text' | 'checkbox' | 'slider' | 'range'>();
    @observable _chosenFacetsCollapse = new ObservableMap<string, boolean>();

    @computed get activeFacets() {
        console.log("chosen collpase " + this._chosenFacetsCollapse)
        const facets = new Map<string, 'text' | 'checkbox' | 'slider' | 'range'>(this._chosenFacets);
        StrListCast(this.targetDoc?._childFilters).map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox'));
        setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox'))));
        return facets;
    }

    /**
     * Responds to clicking the check box in the flyout menu
     */
    @action
    facetClick = (facetHeader: string) => {
        if (!this.targetDoc) return;
        const allCollectionDocs = this.targetDocChildren;
        const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader);

        let nonNumbers = 0;
        let minVal = Number.MAX_VALUE,
            maxVal = -Number.MAX_VALUE;
        facetValues.strings.map(val => {
            const num = val ? Number(val) : Number.NaN;
            if (Number.isNaN(num)) {
                val && nonNumbers++;
            } else {
                minVal = Math.min(num, minVal);
                maxVal = Math.max(num, maxVal);
            }
        });
        if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) {
            this._chosenFacets.set(facetHeader, 'text');
        } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
        } else {
            this._chosenFacets.set(facetHeader, 'checkbox');
        }
        this._chosenFacetsCollapse.set(facetHeader, false)
    };

    facetValues = (facetHeader: string) => {
        const allCollectionDocs = new Set<Doc>();
        SearchBox.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
        const set = new Set<string>([String.fromCharCode(127) + '--undefined--']);
        if (facetHeader === 'tags')
            allCollectionDocs.forEach(child =>
                StrListCast(child[facetHeader])
                    .filter(h => h)
                    .forEach(key => set.add(key))
            );
        else
            allCollectionDocs.forEach(child => {
                const fieldVal = child[facetHeader] as Field;
                set.add(Field.toString(fieldVal));
                (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
            });
        const facetValues = Array.from(set).filter(v => v);

        let nonNumbers = 0;

        facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
        return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2));
    };

    // change css and make the currently selected filters appear at the top

    render() {
        const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));

        return (
            <div className="filterBox-treeView">
                <div className="filterBox-select">
                    <div style={{ width: '100%' }}>
                        <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
                    </div>
                    {/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
                    {/* <div className="filterBox-select-bool"> 
                        <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}>
                            {['AND', 'OR'].map(bool => (
                                <option value={bool} key={bool}>
                                    {bool}
                                </option>
                            ))}
                        </select>
                    </div>{' '} */}
                </div>

                <div className="filterBox-tree" key="tree">
                    {Array.from(this.activeFacets.keys()).map(facetHeader => (
                        <div>
                            <div className  = "filterBox-facetHeader">
                                <div className = "filterBox-facetHeader-Header"> </div>
                                {facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)}
                                
                                <div className = "filterBox-facetHeader-collapse"> 
                                    <div onClick = {action((e) => { 
                                        const collapseBoolValue = this._chosenFacetsCollapse.get(facetHeader)
                                        this._chosenFacetsCollapse.set(facetHeader, !collapseBoolValue )})}>
                                             <AiOutlineMinusSquare/></div>
                                
                                    <div onClick = {action((e) => { 
                                     
                                        // delete this.activeFacets[facetHeader];
                                         this.activeFacets.delete(facetHeader)
                                         this._chosenFacets.delete(facetHeader)
                                         //console.log("TRYING SOMETHING NEW " + e.target.checked)
                                         
                                        }
                                        )
                                    }  
                                    // onChange = {undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, 'check'), 'set filter')}
                                     >
                                             <CiCircleRemove/> </div> 
                                  </div>
                               
                            </div>
                           
                            { this._chosenFacetsCollapse.get(facetHeader) ? null : this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader) }
                            {/*  */}
                        </div>
                    ))}
                </div>
            </div>
        );
    }

    private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode {
        console.log("im here")
        switch (type) {
            case 'text':
                return (
                    <input
                        placeholder={
                            StrListCast(this.targetDoc._childFilters)
                                .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader)
                                ?.split(Doc.FilterSep)[1] ?? '-empty-'
                        }
                        onBlur={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
                        onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
                    />
                );
            case 'checkbox':
                // console.log("checking")
                return this.facetValues(facetHeader).map(fval => {
                    const facetValue = fval;
                    return (
                        <div>
                            <input
                                style={{ width: 20, marginLeft: 20 }}
                                checked={
                                    StrListCast(this.targetDoc._childFilters)
                                        .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue)
                                        ?.split(Doc.FilterSep)[2] === 'check'
                                }
                                type={type}
                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')}
                                onClick={undoable (e => 
                                    console.log("boolean value with " + facetValue + ":" + e.target.checked), 'set filter'
                                ) }
                            />
                            {facetValue}
                        </div>
                    );
                });
        }
    }
}
