const TREE_ELEM_ID = 'markDirTree',
    TREE_PARENT_ELEM_ID = 'markDirBox',
    MARK_BOX_ELEM_ID = 'mark_box',
    MARK_CONTAINER_ELEM_ID = 'mark-container',
    TAG_CHOOSE_CLASS = 'choose',
    FORM_TYPE = 'fileUploadForm',
    TAG_CONTAINER_CLASS = 'tag-container';

let cite_selector = `cite.${TAG_CHOOSE_CLASS}`, tag_container_selector = cite_selector + ' .' + TAG_CONTAINER_CLASS;

class Entity {
    constructor(entityId, choose) {
        this.id = entityId;
        this.choose = choose;
        this.marks = new Map();
    }
}

class Mark {
    constructor(markType, text, choose) {
        this.formType = FORM_TYPE
        this.markType = markType
        this.markValue = text;
        this.choose = choose;
        this.markId = markType + '-' + text
    }

    setEntityId(entityId) {
        this.entityId = entityId;
    }

    get() {
        let {entityId, formType, markType, markValue} = this;
        return {entityId, formType, markType, markValue};
    }

    isChoose() {
        return this.choose;
    }
}

/**
 * 数据资源目录统一置标组件
 */
class DirMarkLayer {
    constructor(directory) {
        this.directory = directory;
        this.choose_list = [];
        this.entitys = new Map();
        this.choose_marks = new Map();
    }

    show() {
        const _this = this;
        layer.open({
            type: 1,
            title: '数据批量置标',
            area: ['1200px'],
            btn: ['保存', '取消'],
            content: `<div id="m-set" style="height: 700px;">
                <div class="m-left" >
                    <div class="layui-card">
                        <div class="layui-card-header">资源目录</div>
                        <div class="layui-card-body" id="${TREE_PARENT_ELEM_ID}">
                            <ul id="${TREE_ELEM_ID}"></ul>
                        </div>
                    </div>
                </div>
                <div class="m-right">
                    <div class="layui-card">
                        <div class="layui-card-header">
                            <label>标签</label>
                            <div class="head-filter">
                                <input class="layui-input" id="filter-input" name="filter" placeholder="输入关键词过滤标签。。。 ">
                                    <i class="layui-icon layui-icon-search"></i>
                                </input>
                            </div>
                        </div>
                        <div class="layui-card-body" id="${MARK_BOX_ELEM_ID}"></div>
                    </div>
                </div>
            </div>`,
            success: function (layerObj, index) {
                _this._initDirTree();
                _this._initMarkBox();
                _this._initMarkTools();
            },
            yes: function (index) {
                _this._save().then(() => layer.close(index));
            },
        })
    }

    _save() {
        const _this = this;
        return new Promise((resolve, reject) => {
            if (_this.choose_marks.size == 0) {
                showErrorMsg('没有可保存的数据');
                return;
            }

            let params = [];
            _this.entitys.forEach((marks) => marks.forEach(mark => params.push(mark)))
            Util.post('/mark/api/record/save/batch', {records: params}).then(res => {
                if (res.flag) {
                    showOkMsg('保存成功');
                    resolve && resolve();
                } else {
                    reject && reject();
                }
            })
        })
    }

    /**
     * 初始化置标标签区域
     * @private
     */
    _initMarkBox() {
        getFileMarkType().then(list => {
            if (Array.isArray(list)) {
                for (let type of list) {
                    this._renderMarkTypeElement(type)
                }
                this._markTypeToolInit();
            }
        })
    }

    /**
     * 渲染置标类型数据元素
     * @param type
     * @private
     */
    _renderMarkTypeElement(type) {
        const _this = this;
        let elemId = MARK_CONTAINER_ELEM_ID + '-' + type.id
        let template = `<div class="type-item">
                <fieldset class="layui-elem-field layui-field-title" style="margin-bottom: 0; height: 20px;">
                    <legend>${type.markType}</legend>
                    <div class="btn-box">
                        <a href="javascript:void(0);" data-type="choose-all">全选</a>
                        <a href="javascript:void(0);" data-type="choose-clear">清空</a>
                        <a href="javascript:void(0);" data-type="box-slide">收起</a>
                    </div>
                </fieldset>
                <div class="mark-container" id="${elemId}"></div>
            </div>`;
        $(`#${MARK_BOX_ELEM_ID}`).append(template);

        let markTag = new MarkTag(tag, {
            tagId: 'type-' + type.id,
            container: '#' + elemId,
            markType: type.id,
            addBtn: true,
            markTypeStr: type.markType,
            insertType: MARK_INSERT_TYPE.FormInsert,
            changeAndSave: true,
            onClick: function (text, $elem) {
                if (_this.choose_list.length === 0) {
                    showErrorMsg('请先选择要置标的数据资源')
                    return
                }
                $elem.toggleClass(TAG_CHOOSE_CLASS);
                let markType = $elem.parent().data('markType');
                let is_choose = $elem.hasClass(TAG_CHOOSE_CLASS);
                let mark = new Mark(markType, text, is_choose);
                if (is_choose) {
                    _this.choose_marks.set(mark.markId, mark);
                } else {
                    _this.choose_marks.delete(mark.markId);
                }
                _this._chooseListSetMarks(mark);
            },
        })

        let marks = [];
        if (type.markDefault) {
            for (let val of type.markDefault.split(',')) {
                marks.push({markType: type.id, markValue: val})
            }
        }
        markTag.setMarkData(marks);
    }

    _markTypeToolInit() {
        let _this = this;
        $('.type-item .btn-box a').on('click', function (event) {
            event.stopPropagation();
            let $root = $(this);
            let btnType = $root.data('type'), $tags = $root.parent().parent().parent().find('.tag-item');
            if (btnType === 'choose-all') {
                if (_this.choose_list.length === 0) {
                    showErrorMsg('请先选择要置标的数据资源')
                    return
                }
                $tags.each((i, elem) => {
                    let $tag = $(elem), data_arr = $tag.data('id').split('-');
                    if ($tag.hasClass(TAG_CHOOSE_CLASS)) {
                        return;
                    }
                    let type = data_arr[0], text = data_arr[1];
                    let mark = new Mark(type, text, true);
                    _this.choose_marks.set(mark.markId, mark);
                    _this._chooseListSetMarks(mark);
                });
                $tags.addClass(TAG_CHOOSE_CLASS);
            }
            if (btnType === 'choose-clear') {
                $tags.removeClass(TAG_CHOOSE_CLASS);
                $tags.each((i, elem) => {
                    let $tag = $(elem), data_id = $tag.data('id');
                    let mark = _this.choose_marks.get(data_id);
                    if (mark) {
                        mark.choose = false;
                        _this._chooseListSetMarks(mark);
                        _this.choose_marks.delete(data_id);
                    }
                });
            }
            if (btnType === 'box-slide') {
                let $container = $root.parent().parent().next()
                $container.slideToggle(0);
                if ($container.is(':visible')) {
                    $root.text('收起');
                } else {
                    $root.text('展开');
                }
            }
        })
    }

    /**
     * 移除指定标签的选中状态
     * @param marks 标签数组
     * @private
     */
    _removeAllMarksChoose() {
        this.choose_marks = new Map();
        $(`.tag-item.${TAG_CHOOSE_CLASS}`).removeClass(TAG_CHOOSE_CLASS);
        $(`cite .tag-item`).remove();
    }

    /**
     * 为选中列表设置标签
     * @param mark
     * @private
     */
    _chooseListSetMarks(mark) {
        let temp_mark = mark;
        this.entitys.forEach(((marks, key) => {
            temp_mark.setEntityId(key);
            if (temp_mark.isChoose()) {
                marks.set(temp_mark.markId, temp_mark.get());
            } else {
                marks.delete(temp_mark.markId);
            }
        }))

        if (mark.isChoose()) {
            this._appendDirMarkTagElem(null, temp_mark);
        } else {
            this._removeDirMarkTagElem(null, temp_mark);
        }
    }

    /**
     * 初始化目录树
     * @private
     */
    _initDirTree() {
        let tree = globalTree.init({
            id: TREE_ELEM_ID,
            url: `${ctx}/business/directory/tree?all=true&id=${this.directory.id}&permis=write`,
            type: 'all',
            cache: false,
            scroll: '#' + TREE_PARENT_ELEM_ID,
            width: '100%',
            checkbar: true,
            checkbarType: 'no-all',
            checkbarFun: {
                chooseBefore: this._chooseBefore,
                chooseDone: nodes => this._chooseDone(nodes)
            },
            done: res => {
            }
        });
    }

    /**
     * 目录树复选框选中完成后的回调函数
     * @param nodes
     */
    _chooseDone(nodes) {
        // 选中后的回调
        nodes = nodes || [];
        let _this = this, ids = nodes.filter(node => {
            let data = node.basicData;
            return getResourceType(data) !== 'directory' && data.id !== DIR_ROOT_ID
        }).map(node => node.basicData.id);

        getActiveEntities(this.choose_list, ids).then(({entities, is_choose}) => {
            if (is_choose) {
                if (entities.length === 1 && ids.length === 1) {
                    _this._removeAllMarksChoose()
                    // 查询数据并渲染标签
                    let entity = entities[0];
                    _fetchMarkByEntity(entity.id).then(markTypes => {
                        if (Array.isArray(markTypes)) {
                            for (let type of markTypes) {
                                if (type.markValue) {
                                    let values = type.markValue.split(',');
                                    for (let val of values) {
                                        let mark = new Mark(type.markType, val, true);
                                        _this.choose_marks.set(mark.markId, mark);
                                    }
                                }
                            }
                            _this._renderMarkChooseState();
                            _append(entities);
                        }
                    }).catch((err) => console.error(err));
                } else {
                    if (_this.entitys.size === 1) {
                        // 移除标签选中样式
                        _this._removeAllMarksChoose()
                        // 移除节点追加的标签元素
                        _remove(_this.entitys);
                    }
                    _append(entities);
                }
            } else {
                _remove(entities);
            }
        })
        this.choose_list = ids;
        if (ids.length === 0) {
            _this._removeAllMarksChoose()
        }

        function _append(entities) {
            for (let en of entities) {
                let temp_marks = new Map(_this.choose_marks.entries());
                temp_marks.forEach((value) => value.setEntityId(en.id))
                _this.entitys.set(en.id, temp_marks);
                _this._appendDirMarkTagElem(en.id);
            }
        }

        function _remove(entities) {
            for (let en of entities) {
                _this.entitys.delete(en.id);
                _this._removeDirMarkTagElem(en.id);
            }
        }

        function _fetchMarkByEntity(entityId) {
            return new Promise((resolve, reject) => {
                Util.get(`/mark/api/record/get/${entityId}`, {}).then(res => {
                    if (res.flag) {
                        resolve && resolve(res.data);
                    } else {
                        showErrorMsg(res.msg);
                    }
                }).catch(reject);
            });
        }
    }

    /**
     * 目录树复选框选中前的回调函数
     * @param $i
     * @param node
     * @returns {boolean}
     */
    _chooseBefore($i, node) {
        if (node.id == DIR_ROOT_ID) {
            showErrorMsg('不能选择根节点！')
            return false;
        }
        return true;
    }

    _appendDirMarkTagElem(entityId, mark) {

        let markValues = [];
        if (mark) {
            markValues.push(mark.markValue)
        } else {
            this.choose_marks.forEach(mark => markValues.push(mark.markValue));
        }

        let template = MarkTag.getStaticTagHtml(markValues.join(','))
        if (entityId) {
            $(`${cite_selector}[data-id="${entityId}"]`).append(template);
        } else {
            $(tag_container_selector).append(template);
        }
    }

    _removeDirMarkTagElem(entityId, mark) {
        if (entityId) {
            $(`cite[data-id="${entityId}"] .tag-item`).remove();
        }
        if (mark) {
            $(`cite .tag-item[data-text="${mark.markValue}"]`).remove();
        }
    }

    _renderMarkChooseState() {
        let _this = this;
        _this.choose_marks.forEach(value => {
            $(`.tag-item[data-id="${value.markId}"]`).addClass(TAG_CHOOSE_CLASS);
        })
    }

    _initMarkTools() {
        // 关键词过滤
        $('#filter-input').on('keyup', function () {
            showSearchMark($(this).val().trim());
        });
    }
}

function showSearchMark(text) {
    let tagItems = $('#mark_box .tag-item'), tagBox = tagItems.parent().parent(),
        addBtn = $('#mark_box .button-new-tag');
    if (text) {
        // hide all
        tagItems.hide();
        addBtn.hide();
        tagBox.hide();

        // show choose
        tagItems.each((index, elem) => {
            let $elem = $(elem);
            if ($elem.text().includes(text)) {
                $elem.parent().parent().show();
                $elem.show()
            }
        })
    } else {
        // show all
        tagBox.show();
        tagItems.show();
        addBtn.show();
    }
}

/**
 * 获取文件上传表单的文件置标配置信息
 * @returns {Promise<unknown>}
 */
function getFileMarkType() {
    return new Promise((resolve, reject) => {
        Util.get(`/mark/api/type/list`, {}).then(res => {
            if (res.flag) {
                resolve && resolve(res.data);
            } else {
                showErrorMsg('置标类型加载失败')
                console.error('置标类型加载失败', res.msg);
            }
        })
    })
}

/**
 * 获取活动的节点，分为新增的选中节点和移除选中的节点
 * @param oldNodes
 * @param newNodes
 * @returns Promise
 */
function getActiveEntities(oldNodes, newNodes = []) {
    return new Promise(resolve => {
        let entities = [], is_choose = false;
        if (oldNodes.length > newNodes.length) {
            is_choose = false;
            oldNodes.filter(item => !newNodes.includes(item)).forEach(item => {
                entities.push(new Entity(item, is_choose))
                $(`cite[data-id="${item}"]`).removeClass(TAG_CHOOSE_CLASS)
            })
        } else {
            is_choose = true;
            newNodes.filter(item => !oldNodes.includes(item)).forEach(item => {
                entities.push(new Entity(item, is_choose));
                $(`cite[data-id="${item}"]`).addClass(TAG_CHOOSE_CLASS)
                let _cite_selector = `${cite_selector}[data-id="${item}"]`;
                let container = $(`${_cite_selector} .${TAG_CONTAINER_CLASS}`);
                if (container.length === 0) {
                    $(_cite_selector).append($(`<div class="tag-container"></div>`));
                }
            })
        }
        resolve && resolve({entities, is_choose});
    });
}

function getResourceType(data) {
    let {directoryName, dataSourceId} = data;
    if (directoryName) {
        return 'directory';
    }
    if (dataSourceId && dataSourceId.startsWith('file_')) {
        return 'file';
    }
    return 'metadata';
}

/**
 * 打开数据批量置标窗口
 * @param parent_dir
 */
function openDirMarkLayer(parent_dir) {
    let markLayer = new DirMarkLayer(parent_dir);
    markLayer.show();
}
