/**
 * 多标签页
 * */
import { get } from 'lodash';
import router from '../../../../router';
import Setting from '../../../../setting';
import { getAllSiderMenu, includeArray } from '../../../../libs/system';

// 判定是否需要缓存
const isKeepAlive = data => get(data, 'meta.cache', false);

export default {
    namespaced: true,
    state: {
        // 可以在多页 tab 模式下显示的页面
        pool: [],
        // 当前显示的多页面列表
        opened: Setting.page.opened,
        // 当前页面
        current: '',
        // 需要缓存的页面 name
        keepAlive: []
    },
    actions: {
        /**
         * @description 从持久化数据载入分页列表
         */
        openedLoad({ state, commit, dispatch, rootState }) {
            return new Promise(async resolve => {
                // store 赋值
                const value = await dispatch(
                    'admin/db/get',
                    {
                        dbName: 'sys',
                        path: 'page.opened',
                        defaultValue: Setting.page.opened,
                        user: true
                    },
                    { root: true }
                );
                // 在处理函数中进行数据优化 过滤掉现在已经失效的页签或者已经改变了信息的页签
                // 以 fullPath 字段为准
                // 如果页面过多的话可能需要优化算法
                // valid 有效列表 1, 1, 0, 1 => 有效, 有效, 失效, 有效
                const valid = [];
                // 处理数据
                state.opened = value
                    .map(opened => {
                        // 忽略首页
                        if (opened.fullPath === '/index') {
                            valid.push(1);
                            return opened;
                        }
                        // 尝试在所有的支持多标签页的页面里找到 name 匹配的页面
                        const find = state.pool.find(item => item.name === opened.name);
                        // 记录有效或无效信息
                        valid.push(find ? 1 : 0);
                        // 返回合并后的数据 新的覆盖旧的
                        // 新的数据中一般不会携带 params 和 query, 所以旧的参数会留存
                        return Object.assign({}, opened, find);
                    })
                    .filter((opened, index) => valid[index] === 1)
                    // 对 menu 鉴权过滤
                    .filter(opened => {
                        let menuSider = this.getters['admin/menu/sider'];

                        const allSiderMenu = getAllSiderMenu(menuSider);
                        const find = allSiderMenu.find(item => item.path === opened.fullPath);

                        let state = true;
                        if (find && find.auth) {
                            const userInfo = rootState.admin.user.info;
                            // @权限
                            const access = userInfo.access;
                            // 如果用户当前的权限，不是该 menu 对应的 权限，则过滤这个 Tab
                            if (access && !includeArray(find.auth, access)) state = false;
                        }
                        return state;
                    });
                // 根据 opened 数据生成缓存设置
                commit('keepAliveRefresh');
                // end
                resolve();
            });
        },
        /**
         * 将 opened 属性赋值并持久化 在这之前请先确保已经更新了 state.opened
         */
        opened2db({ state, dispatch }) {
            return new Promise(async resolve => {
                // 设置数据
                dispatch(
                    'admin/db/set',
                    {
                        dbName: 'sys',
                        path: 'page.opened',
                        value: state.opened,
                        user: true
                    },
                    { root: true }
                );
                // end
                resolve();
            });
        },
        /**
         * @description 更新页面列表上的某一项
         * @param {Object} param { index, params, query, fullPath } 路由信息
         */
        openedUpdate({ state, commit, dispatch }, { index, params, query, fullPath, meta }) {
            return new Promise(async resolve => {
                // 更新页面列表某一项
                let page = state.opened[index];
                page.params = params || page.params;
                page.query = query || page.query;
                page.fullPath = fullPath || page.fullPath;
                page.meta = meta || page.meta;
                state.opened.splice(index, 1, page);
                // 持久化
                await dispatch('opened2db');
                // end
                resolve();
            });
        },
        /**
         * @description 更新页面当前项
         * @param {Object} param { params, query, fullPath } 路由信息
         */
        currentUpdate({ state, commit, dispatch }, { params, query, fullPath, meta }) {
            return new Promise(async resolve => {
                // 更新当前项
                const index = state.opened.findIndex(item => item.fullPath === state.current);
                let page = state.opened[index];
                page.params = params || page.params;
                page.query = query || page.query;
                page.fullPath = fullPath || page.fullPath;
                page.meta = meta || page.meta;
                state.opened.splice(index, 1, page);
                // 持久化
                await dispatch('opened2db');
                // end
                resolve();
            });
        },
        /**
         * @description 新增一个 tag (打开一个页面)
         * @param {Object} param new tag info
         */
        add({ state, commit, dispatch }, { tag, params, query, fullPath }) {
            return new Promise(async resolve => {
                // 设置新的 tag 在新打开一个以前没打开过的页面时使用
                let newTag = tag;
                newTag.params = params || newTag.params;
                newTag.query = query || newTag.query;
                newTag.fullPath = fullPath || newTag.fullPath;
                // 添加进当前显示的页面数组
                state.opened.push(newTag);
                // 如果这个页面需要缓存 将其添加到缓存设置
                if (isKeepAlive(newTag)) {
                    commit('keepAlivePush', tag.name);
                }
                // 持久化
                await dispatch('opened2db');
                // end
                resolve();
            });
        },
        /**
         * @description 打开一个新的页面
         * @param {Object} param 从路由钩子的 to 对象上获取 { name, params, query, fullPath } 路由信息
         */
        open({ state, commit, dispatch }, { name, params, query, fullPath }) {
            return new Promise(async resolve => {
                // 已经打开的页面
                let opened = state.opened;
                // 判断此页面是否已经打开 并且记录位置
                let pageOpenedIndex = 0;
                const pageOpened = opened.find((page, index) => {
                    const same = page.fullPath === fullPath;
                    pageOpenedIndex = same ? index : pageOpenedIndex;
                    return same;
                });
                if (pageOpened) {
                    // 页面以前打开过
                    await dispatch('openedUpdate', {
                        index: pageOpenedIndex,
                        params,
                        query,
                        fullPath
                    });
                } else {
                    // 页面以前没有打开过
                    let page = state.pool.find(t => t.name === name);
                    // 如果这里没有找到 page 代表这个路由虽然在框架内 但是不参与标签页显示
                    if (page) {
                        await dispatch('add', {
                            tag: Object.assign({}, page),
                            params,
                            query,
                            fullPath
                        });
                    }
                }
                commit('currentSet', fullPath);
                // end
                resolve();
            });
        },
        /**
         * @description 关闭一个 tag (关闭一个页面)
         * @param {Object} param { tagName: 要关闭的标签名字 }
         */
        close({ state, commit, dispatch }, { tagName }) {
            return new Promise(async resolve => {
                // 下个新的页面
                let newPage = state.opened[0];
                const isCurrent = state.current === tagName;
                // 如果关闭的页面就是当前显示的页面
                if (isCurrent) {
                    // 去找一个新的页面
                    let len = state.opened.length;
                    for (let i = 0; i < len; i++) {
                        if (state.opened[i].fullPath === tagName) {
                            // 是否只剩最后一个，是则跳首页
                            if (len > 1) {
                                // 如果是最后一个，则向前一个跳，否则向下一个跳
                                if (i === len - 1) {
                                    newPage = state.opened[i - 1];
                                } else {
                                    newPage = state.opened[i + 1];
                                }
                            } else {
                                newPage = {};
                            }
                            break;
                        }
                    }
                }
                // 找到这个页面在已经打开的数据里是第几个
                const index = state.opened.findIndex(page => page.fullPath === tagName);
                if (index >= 0) {
                    // 如果这个页面是缓存的页面 将其在缓存设置中删除
                    commit('keepAliveRemove', state.opened[index].name);
                    // 更新数据 删除关闭的页面
                    state.opened.splice(index, 1);
                }
                // 持久化
                await dispatch('opened2db');
                // 最后需要判断是否需要跳到首页
                if (isCurrent) {
                    const { name = 'index', params = {}, query = {} } = newPage;
                    let routerObj = {
                        name,
                        params,
                        query
                    };
                    router.push(routerObj, () => {});
                }
                // end
                resolve();
            });
        },
        /**
         * @description 关闭当前标签左边的标签
         * @param {Object} param { pageSelect: 当前选中的tagName }
         */
        closeLeft({ state, commit, dispatch }, { pageSelect } = {}) {
            return new Promise(async resolve => {
                const pageAim = pageSelect || state.current;
                let currentIndex = 0;
                state.opened.forEach((page, index) => {
                    if (page.fullPath === pageAim) {
                        currentIndex = index;
                    }
                });
                if (currentIndex > 0) {
                    // 删除打开的页面 并在缓存设置中删除
                    state.opened.splice(1, currentIndex - 1).forEach(({ name }) => commit('keepAliveRemove', name));
                }
                state.current = pageAim;
                if (router.app.$route.fullPath !== pageAim) {
                    router.push(pageAim);
                }
                // 持久化
                await dispatch('opened2db');
                // end
                resolve();
            });
        },
        /**
         * @description 关闭当前标签右边的标签
         * @param {Object} param { pageSelect: 当前选中的tagName }
         */
        closeRight({ state, commit, dispatch }, { pageSelect } = {}) {
            return new Promise(async resolve => {
                const pageAim = pageSelect || state.current;
                let currentIndex = 0;
                state.opened.forEach((page, index) => {
                    if (page.fullPath === pageAim) {
                        currentIndex = index;
                    }
                });
                // 删除打开的页面 并在缓存设置中删除
                state.opened.splice(currentIndex + 1).forEach(({ name }) => commit('keepAliveRemove', name));
                // 设置当前的页面
                state.current = pageAim;
                if (router.app.$route.fullPath !== pageAim) {
                    router.push(pageAim);
                }
                // 持久化
                await dispatch('opened2db');
                // end
                resolve();
            });
        },
        /**
         * @description 关闭当前激活之外的 tag
         * @param {Object} param { pageSelect: 当前选中的tagName }
         */
        closeOther({ state, commit, dispatch }, { pageSelect } = {}) {
            return new Promise(async resolve => {
                const pageAim = pageSelect || state.current;
                let currentIndex = 0;
                state.opened.forEach((page, index) => {
                    if (page.fullPath === pageAim) {
                        currentIndex = index;
                    }
                });
                // 删除打开的页面数据 并更新缓存设置
                if (currentIndex === 0) {
                    state.opened.splice(1).forEach(({ name }) => commit('keepAliveRemove', name));
                } else {
                    state.opened.splice(currentIndex + 1).forEach(({ name }) => commit('keepAliveRemove', name));
                    state.opened.splice(1, currentIndex - 1).forEach(({ name }) => commit('keepAliveRemove', name));
                }
                // 设置新的页面
                state.current = pageAim;
                if (router.app.$route.fullPath !== pageAim) {
                    router.push(pageAim);
                }
                // 持久化
                await dispatch('opened2db');
                // end
                resolve();
            });
        },
        /**
         * @description 关闭所有 tag
         * @param {Object} state vuex state
         */
        closeAll({ state, commit, dispatch }) {
            return new Promise(async resolve => {
                // 删除打开的页面 并在缓存设置中删除
                state.opened.splice(1).forEach(({ name }) => commit('keepAliveRemove', name));
                // 持久化
                await dispatch('opened2db');
                // 关闭所有的标签页后需要判断一次现在是不是在首页
                if (router.app.$route.name !== 'index') {
                    router.push(
                        {
                            name: 'index'
                        },
                        () => {}
                    );
                }
                // end
                resolve();
            });
        }
    },
    mutations: {
        /**
         * @class keepAlive
         * @description 从已经打开的页面记录中更新需要缓存的页面记录
         * @param {Object} state vuex state
         */
        keepAliveRefresh(state) {
            state.keepAlive = state.opened.filter(item => isKeepAlive(item)).map(e => e.name);
        },
        /**
         * @description 删除一个页面的缓存设置
         * @param {Object} state vuex state
         * @param {String} name name
         */
        keepAliveRemove(state, name) {
            const list = [...state.keepAlive];
            const index = list.findIndex(item => item === name);

            if (index !== -1) {
                list.splice(index, 1);
                state.keepAlive = list;
            }
        },
        /**
         * @description 增加一个页面的缓存设置
         * @param {Object} state vuex state
         * @param {String} name name
         */
        keepAlivePush(state, name) {
            const keep = [...state.keepAlive];
            keep.push(name);
            state.keepAlive = keep;
        },
        /**
         * @description 清空页面缓存设置
         * @param {Object} state vuex state
         */
        keepAliveClean(state) {
            state.keepAlive = [];
        },
        /**
         * @class current
         * @description 设置当前激活的页面 fullPath
         * @param {Object} state vuex state
         * @param {String} fullPath new fullPath
         */
        currentSet(state, fullPath) {
            state.current = fullPath;
        },
        /**
         * @class pool
         * @description 保存 pool (候选池)
         * @param {Object} state vuex state
         * @param {Array} routes routes
         */
        init(state, routes) {
            const pool = [];
            const push = function(routes) {
                routes.forEach(route => {
                    if (route.children) {
                        push(route.children);
                    } else {
                        if (!route.hidden) {
                            const { meta, name, path } = route;
                            pool.push({ meta, name, path });
                        }
                    }
                });
            };
            push(routes);
            state.pool = pool;
        }
    }
};
