// redux dependencies
import { connect } from 'react-redux'
import createReducer from 'lib/reducer';
import backendRequest, { download } from 'lib/backend';


// react component dependencies
import React, { Component } from 'react'
import Select from 'react-select';

import { upload } from 'lib/backend';

import { defineMessages, injectIntl, FormattedDate } from 'react-intl'
import messages from './messages';

require("./tag.scss");

export const tagKey = (objType = "objType", objId = "objId") => {
    return [objType, objId].join(":");
};


/* REDUX */
const initialState = {};
const emptyList = {
    status: null,
    data: []
};

let _reducer = createReducer(initialState);

function registerComponent(unitId, objectType, objectId) {
    return {
        type: '@@tags/REGISTER',
        payload: tagKey(objectType, objectId),
        meta: {
            unitId,
            objectType,
            objectId
        }
    }
}


function unregisterComponent(unitId, objectType, objectId) {
    return {
        type: '@@tags/UNREGISTER',
        payload: tagKey(objectType, objectId),
        meta: {
            unitId,
            objectType,
            objectId
        }
    }
}


function fetchTags(unitId, objectType, objectId) {
    return {
        type: '@@tags/FETCH',
        payload: backendRequest(`/api/${unitId}/tags/${objectType}/${objectId}`),
        meta: {
            key: tagKey(objectType, objectId),
            unitId,
            objectType,
            objectId
        }
    }
}


function addTag(unitId, objectType, objectId, tag) {
    return {
        type: '@@tags/ADD',
        payload: backendRequest(`/api/${unitId}/tags/${objectType}/${objectId}`, "PUT", tag),
        meta: {
            key: tagKey(objectType, objectId),
            unitId,
            objectType,
            objectId,
            tag
        }
    }
}


function deleteTag(unitId, objectType, objectId, tagId) {
    return {
        type: '@@tags/FETCH',
        payload: backendRequest(`/api/${unitId}/tags/${objectType}/${objectId}/${tagId}`, "DELETE"),
        meta: {
            unitId,
            objectType,
            objectId,
            tagId
        }
    }
}


_reducer.handleAction("@@tags/REGISTER", (state, action) => state.updateIn([action.payload, "count"], val => isNaN(val) ? 1 : val + 1 ));
_reducer.handleAction("@@tags/UNREGISTER", (state, action) => {
    let count = state.getIn([action.payload, "count"], 0);
    if (count < 2) {
        state = state.remove(action.payload);
    } else {
        state = state.updateIn([action.payload, "count"], val => val - 1);
    }
    return state;
} );



_reducer.handleAction("@@tags/FETCH_PENDING", (state, action) => state.setIn([action.meta.key, "list", "status"], "pending") );
_reducer.handleAction("@@tags/FETCH_FULFILLED", (state, action) => state.setIn([action.meta.key, "list", "status"], "success").setIn([action.meta.key, "list", "data"], action.payload.body) );
_reducer.handleAction("@@tags/FETCH_REJECTED", (state, action) => state.setIn([action.meta.key, "list", "status"], "failure").deleteIn([action.meta.key, "list", "data"]) );



//export reducer
export const reducer = _reducer.export();


/* ******************************************* */
/* UI Components                               */
/* ******************************************* */

//require("./tag.scss")



class Tags extends Component {

    constructor(props) {
        super(props);

        this.state = {};

        this.deleteRow = this.deleteRow.bind(this);

        this.handleChange = this.handleChange.bind(this);
        this.loadOptions = this.loadOptions.bind(this);
    }


    async deleteRow(id) {
        await this.props.deleteTag(id);
        await this.props.fetchData();
    }



    componentWillReceiveProps(props) {
        //clearInfoMessages(this);
        if (props.objectId != this.props.objectId) {
            this.props.fetchData();
        }
    }


    //TODO ensure data
    componentWillMount() {
        this.props.registerComponent();

        //refresh list?
        const { status, objectId } = this.props;
        if (status !== "pending" && objectId) {
            this.props.fetchData();
        }
    }
    

    componentWillUnmount() {
        this.props.unregisterComponent();
    }
    

    async handleChange(tags) {
        let { data, fetchData, unitId, objectType, objectId } = this.props;
        if (!Array.isArray(data)) {
            data = [];
        }

        let toDelete = data.slice();
        let toCreate = [];

        outer:
        for (let t of tags) {
            let i = toDelete.length;
            while (i--) {
                let d = toDelete[i];
                if (t.id != t.tag && d.id == t.id) { 
                    toDelete.splice(i, 1);
                    continue outer;
                } 
            }

            await backendRequest(`/api/${unitId}/tags/${objectType}/${objectId}`, "PUT", { body: { tag: t.tag } });
        }

        if (toDelete.length) {
            for (let d of toDelete) {
                await backendRequest(`/api/${unitId}/tags/${objectType}/${objectId}/${d.id}`, "DELETE");
            }
        }

        await fetchData();
    }

    async loadOptions(filter) {
        const { unitId, objectType, objectId } = this.props;
        if (!objectId) {
            return { options: [] };
        }
        let response = await backendRequest(`/api/${unitId}/tags/${objectType}/${objectId}/options?q=${encodeURIComponent(filter)}`, "GET");
        if (!response || !Array.isArray(response.body)) {
            return ({ options: [] });
        }
        return ({ options: response.body });
    }


    render() {
        let { 
            status, 
            error, 
            data,
            editable = true,
            disabled,
            unitId, 
            objectType, 
            objectId, 
            intl,
            icon = ""
        } = this.props;

        
        return (
            <div className={`tags ${this.props.className||""}`}>
                <h3>
                    <i className="icon">{ icon }</i> { intl.formatMessage(messages.title) }
                </h3>

                <Select.AsyncCreatable
                    value={ data || [] }
                    onChange={ this.handleChange }
                    loadOptions={ this.loadOptions }
                    multi={ true }
                    valueKey="id"
                    labelKey="tag"
                    enabled={ status != "pending" }
                    disabled={ status == "pending" || !objectId }
                    />
            </div>
            );
    }
}
/*
*/


const mapStateToProps = (state, props) => {
    let s = state.getIn(["tag", tagKey(props.objectType, props.objectId), "list"]);
    if (!s) {
        return emptyList;
    }
    return s.toJS();
}



function mapDispatchToProps(dispatch, props) {
    return {
        registerComponent: () => dispatch(registerComponent(props.unitId, props.objectType, props.objectId)),
        unregisterComponent: () => dispatch(unregisterComponent(props.unitId, props.objectType, props.objectId)),
        fetchData: () => dispatch(fetchTags(props.unitId, props.objectType, props.objectId)),
        deleteTag: (tagId) => dispatch(deleteTag(props.unitId, props.objectType, props.objectId, tagId)),
        addTag: (tag) => dispatch(addTag(props.unitId, props.objectType, props.objectId, tag)),
    };
}


Tags = injectIntl(connect(mapStateToProps, mapDispatchToProps)(Tags));

export default Tags;

