/**
 * auth: 允许的权限
 * authExclude: 排除的权限
 * 
 * auth的几种传值方式
 * 1.String
 *      例: auth="sysApp:page"
 *      直接传入字符串,对单项权限进行验证
 * 
 * 2.Array
 *  2.1.单项权限
 *      例: :auth="['sysApp:page']"
 *  2.2.并且关系多项权限
 *      例: :auth="['sysApp:page', 'sysApp:add']"
 *      数组中传入多个字符串
 *      此时验证的是同时拥有"sysApp:page"和"sysApp:add"两项权限才会渲染
 *  2.3.或者关系多项权限
 *      例: :auth="[['sysApp:page', 'sysApp:add'], ['sysApp:edit']]"
 *      二维数组结构,内部数组之间为并且关系
 *      此时验证的是"sysApp:page"&"sysApp:add"||"sysApp:edit"
 *      注意:或者的条件必须包括在数组中,暴露在外则判定为并且
 *  2.4.可直接传入布尔值
 *      例: :auth="['sysApp:page', 1 === 1]"
 *          :auth="[['sysApp:page', 'sysApp:add'], [1 === 1]]"
 * 
 * 3.Json
 *  如果觉得多项权限时每次都要写应用编号比较繁琐,可对Array形式进行简化
 *  3.1.单项权限
 *      例: :auth="{ sysApp: 'page' }"
 *  3.2.并且关系多项权限
 *      例: :auth="{ sysApp: ['page', 'add'] }"
 *  3.3.或者关系多项权限
 *      例: :auth="{ sysApp: [['page', 'add'], ['edit']]}"
 *  3.4.可直接传入布尔值
 *      例: :auth="{ sysApp: ['page', 1 === 1] }"
 *          :auth="{ sysApp: [['page', 'add'], [1 === 1]] }"
 * 
 */

import app from '@/main'

const authByArray = (auth, permissions) => {

    const flags = []

    auth.forEach(p => {
        switch (p.constructor) {
            case String:
                flags.push([permissions.indexOf(p) > -1, '&&'])
                break
            case Array:
                flags.push([authByArray(p, permissions), '||'])
                break
            case Boolean:
                flags.push([p, '&&'])
                break
        }
    })

    let result

    flags.forEach((p, i) => {
        if (p[1] === '&&') {
            if (i === 0) {
                result = true
            }
            if (result) {
                result = p[0]
            }
        } else {
            if (i === 0) {
                result = false
            }
            if (!result) {
                result = p[0]
            }
        }
        //result = p[1] === '&&' ? result && p[0] : result || p[0]
    })

    return result
}

const authByJson = (auth, permissions) => {

    let result = true

    const flags = []

    const deepName = (arr, key) => {
        arr.forEach((p, i) => {
            switch (p.constructor) {
                case String:
                    arr[i] = `${key}:${p}`
                    break
                case Array:
                    p = deepName(p, key)
                    break
                default:
                    p = p
                    break
            }
        })
        return arr
    }

    for (let key in auth) {
        const app = auth[key]
        switch (app.constructor) {
            case String:
                flags.push(permissions.indexOf(`${key}:${p}`) > -1)
                break
            case Array:
                flags.push(authByArray(deepName(app, key), permissions))
                break
        }
    }

    flags.forEach(p => {
        result = result && p
    })

    return result

}

export const auth = (auth) => {

    const { info } = app.global

    if (!info) {
        return false
    }

    /**
     * 超级管理员
     */
    if (info.adminType === 1) {
        return true
    }

    const permissions = info.permissions

    let flag = false

    if (auth) {
        switch (auth.constructor) {
            case String:
                flag = permissions.indexOf(auth) > -1
                break
            case Array:
                flag = authByArray(auth, permissions)
                break
            case Object:
                flag = authByJson(auth, permissions)
                break
        }
    }

    return flag
}

export default {
    functional: true,
    props: {
        auth: {
            default() {
                return new Array()
            },
            type: [Array, Object, String],
        },
        authExclude: {
            default() {
                return new Array()
            },
            type: Array,
        },
    },

    render(h, context) {
        const { props, scopedSlots } = context
        const authExclude = props.authExclude

        let flag = auth(props.auth)

        if (flag) {
            return scopedSlots.default()
        }

        return false
    },
}