import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import AnnotationUtil from '../../util/AnnotationUtil';
import IDUtil from '../../util/IDUtil';
import AnnotationAPI from '../../api/AnnotationAPI';
import Project from '../../model/Project';
import ProjectList from "../workspace/projects/ProjectList";

class BookmarkSelector extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            bookmarkGroups: [],
            selectedGroups: {},
            emptyFieldNameError: false,
            nameExistsError: false,
            selectedProject: this.props.project
        };
        this.CLASS_PREFIX = 'bms';
        this.newGroupRef = React.createRef();
    }

    componentDidMount() {
        this.reloadBookmarkGroupList(this.props.user, this.state.selectedProject);
    }

    // load all "bookmark group annotations" of current user project
    reloadBookmarkGroupList = (user, project) => {
        const filter = {
            'user.keyword': user.id,
            'motivation': 'bookmarking'
        };
        if (project) {
            filter['project'] = project.id;
        }
        AnnotationAPI.getFilteredAnnotations(
            user.id,
            filter,
            null, //not_filters
            this.onLoadBookmarkAnnotations
        );
    }

    /*--------------------------------------------- ON LOAD FUNCTIONS -------------------------------- */

    onLoadBookmarkAnnotations = bookmarkGroups => {
        if (this.props.resourceId) {
            //only when opened from the resource viewer (flags the groups the resource is a member of)
            this.updateGroupMembership(this.props.resourceId, bookmarkGroups);
        } else {
            //only when opened from the search results/selection list
            this.setState({ bookmarkGroups: bookmarkGroups || [] });
        }
    };

    //update this.state.selectedGroups with the groups the specified resource is a member of
    updateGroupMembership = (resourceId, bookmarkGroups) => {
        const filter = {
            'target.selector.value.id': resourceId,
            'user.keyword': this.props.user.id,
            motivation: 'bookmarking'
        };
        if (this.state.selectedProject && this.state.selectedProject.id) {
            filter['project'] = this.state.selectedProject.id;
        }
        AnnotationAPI.getFilteredAnnotations(
            this.props.user.id,
            filter,
            null,
            this.onUpdateGroupMembership.bind(this, bookmarkGroups),
            0, //offset
            250, //size
            null, //sort direction
            null //date range
        );
    };

    onUpdateGroupMembership(bookmarkGroups, resourceGroups) {
        const selectedGroups = {};
        bookmarkGroups.forEach(group => {
            if (
                resourceGroups.findIndex(
                    resourceGroup => resourceGroup.id === group.id
                ) !== -1
            ) {
                selectedGroups[group.id] = true;
            }
        });
        this.setState({
            bookmarkGroups: bookmarkGroups,
            selectedGroups: selectedGroups
        });
    }

    /*--------------------------------------------- USER TRIGGERED FUNCTIONS -------------------------------- */

    //selects or deselects a bunch of groups
    toggleBookmarkGroup(group) {
        const selectedGroups = this.state.selectedGroups;
        if (selectedGroups[group.id] === true) {
            delete selectedGroups[group.id];
        } else {
            selectedGroups[group.id] = true;
        }
        this.setState({
            selectedGroups: selectedGroups,
            emptyFieldNameError: false, //reset both errors when toggling a group
            nameExistsError: false
        });
    }

    addNewBookmarkGroup = (e, callback) => {
        e.preventDefault();
        const nameExist = this.state.bookmarkGroups.some(item =>
            item.body.some(it => it.label === this.newGroupRef.current.value)
        );

        if (!this.newGroupRef.current.value.trim().length) {
            //show error msg
            this.setState({
                emptyFieldNameError: true,
                nameExistsError: false
            });
            return false;
        } else if (nameExist) {
            this.setState({
                nameExistsError: true,
                emptyFieldNameError: false
            });
            return false;
        }

        const bookmarkGroup = AnnotationUtil.generateBookmarkGroupAnnotation(
            this.props.user,
            this.state.selectedProject,
            this.props.collectionId,
            this.newGroupRef.current.value.trim()
        );

        //update the state and reset the text field to ''.
        this.setState(
            {
                bookmarkGroups: this.state.bookmarkGroups.concat([
                    bookmarkGroup
                ]), //add the new bookmarkgroup
                emptyFieldNameError: false,
                selectedGroups: {
                    ...this.state.selectedGroups,
                    ...{ [bookmarkGroup.id]: true }
                } //add the new group to the selected groups
            },
            () => {
                this.newGroupRef.current.value = '';
                if (callback && typeof callback === 'function') {
                    callback(); //callback the owner
                }
            }
        );
    };

    submitForm = e => {
        //if the user entered a bookmark group name, try to add it & subsequently save it
        if (this.newGroupRef.current.value && this.newGroupRef.current.value.trim().length > 0) {
            this.addNewBookmarkGroup(e, this.onOutput);
        } else { //only allow saving when a bookmark group is selected
            if(this.state.selectedGroups && Object.keys(this.state.selectedGroups).length > 0) {
                this.onOutput();
            }
        }
    };

    /*--------------------------------------------- COMMUNICATE BACK TO THE OWNER -------------------------------- */

    //communicate back a multi-target annotation with a classification body
    onOutput = () => {
        if (this.props.onOutput) {
            //returns all bookmark groups (multi-target annotations) + which have been selected
            this.props.onOutput(this.constructor.name, {
                allGroups: this.state.bookmarkGroups,
                selectedGroups: this.state.selectedGroups,
                selectedProject: this.state.selectedProject
            });
        }
    };

    onSelectProject = project => {
        if(project && project.name) {
            this.setState({
                selectedProject: project
            }, this.reloadBookmarkGroupList.bind(this, this.props.user, project))
        }
    };

    /*--------------------------------------------- RENDER FUNCTIONS -------------------------------- */

    renderProjectList = (activeProject, userProjects, user, onSelectProject) => (
        <div
            title={activeProject ? "Current user project. Click to change." : ""}
            style={{float: activeProject ? 'right' : 'none'}}
        >
            <ProjectList
                buttonText="Set active project"
                activeProject={activeProject}
                onSelect={onSelectProject}
                projects={userProjects}
                user={user}
                projectIcon={false}
            />
        </div>
    );

    renderBookmarkGroupForm = (bookmarkGroupList) => (
        <div className={IDUtil.cssClassName('bgroup-form', this.CLASS_PREFIX)}>
            {bookmarkGroupList && <h4>Your bookmark groups</h4>}
            {bookmarkGroupList}

            <h4>Create bookmark group</h4>
            <div className={IDUtil.cssClassName('new-bgroup', this.CLASS_PREFIX)}>
                <form onSubmit={this.addNewBookmarkGroup}>
                    <input ref={this.newGroupRef} type="text" aria-label="Create new bookmark group"/>
                    <button type="submit" className="btn btn-default">
                        Add
                    </button>
                </form>
            </div>
        </div>
    );

    renderBookmarkGroupList = (bookmarkGroups, selectedGroups) => {
        //TODO which part of the body is the name of the bookmark group?
        const options = bookmarkGroups
            .sort((a, b) => {
                const nameA = a.body[0].label.toUpperCase(); // ignore upper and lowercase
                const nameB = b.body[0].label.toUpperCase(); // ignore upper and lowercase
                if (nameA < nameB) {
                    return -1;
                }
                if (nameA > nameB) {
                    return 1;
                }

                // names must be equal
                return 0;
            })
            .map((group, index) => {
                return (
                    <a
                        className={classNames('list-group-item', {
                            selected: selectedGroups[group.id]
                        })}
                        href="#"
                        key={'an__' + index}
                        onClick={this.toggleBookmarkGroup.bind(this, group)}
                    >
                        <i className="fas fa-bookmark" />
                        {group.body[0].label}
                        <span className="member-count" title="Bookmarks">
                            {group.target.length}
                        </span>
                    </a>
                );
            });
        return <div className="list-group">{options}</div>;
    };

    renderErrorMsg = (emptyFieldNameError, nameExistsError, selectedGroups) => {
        let msg = null;
        if (emptyFieldNameError === true) {
            msg = 'Please enter a label for the bookmark group';
        } else if (nameExistsError === true) {
            msg = 'The label you entered already exists';
        } else if(selectedGroups == null || Object.keys(selectedGroups).length === 0) {
            msg = 'Please create or select a bookmark group to save your selection to';
        }
        return msg ? <div className="validation-error">{msg}</div> : null;
    };

    render() {
        let bookmarkGroupForm = null;
        let errorMsg = null;
        const projectList = this.renderProjectList(
            this.state.selectedProject,
            this.props.userProjects,
            this.props.user,
            this.onSelectProject
        );

        if(this.state.selectedProject) {
            const bookmarkGroupList = this.state.bookmarkGroups.length > 0 ?
                this.renderBookmarkGroupList(
                    this.state.bookmarkGroups,
                    this.state.selectedGroups
                ) : null
            ;

            bookmarkGroupForm = this.renderBookmarkGroupForm(bookmarkGroupList);

            errorMsg = this.renderErrorMsg(
                this.state.emptyFieldNameError,
                this.state.nameExistsError,
                this.state.selectedGroups
            );
        }

        return (
            <div className={IDUtil.cssClassName('bookmark-selector')}>
                <div className="user-input">
                    {bookmarkGroupForm}
                    {projectList}
                </div>
                <div className="save-btn">
                    <button className="btn btn-primary" onClick={this.submitForm}>
                        Save
                    </button>
                </div>
                {errorMsg}
            </div>
        );
    }
}

BookmarkSelector.propTypes = {
    collectionId: PropTypes.string.isRequired,
    onOutput: PropTypes.func.isRequired,
    project: Project.getPropTypes(false),
    userProjects : PropTypes.array,
    user: PropTypes.shape({
        id: PropTypes.string.isRequired,
        name: PropTypes.string,
        attributes: PropTypes.shape({
            allowPersonalCollections: PropTypes.bool
        })
    })
};
export default BookmarkSelector;
