import { createID, debugLog, getItemCleanStart, getSourceFromTransaction, Item, Transaction, YArray, YMap } from "../internals"
import { isPlainObject } from "./signature/utils"

/**
 * 
 * @returns {{
*  [key: string]: any
* }}
*/
export const getDefaultDocAuthConfig = () => {
    return {
        check: false, // 是否要进行权限控制
        currentUser: '',
        checkSource: {
            local: false, // 来自local的item，如执行操作得到的item
            decoder: false, // 来自远程同步得到的item
            level: false // 来自levelDB的item
        },
        // YArray | () => YArray
        roles: new YArray(),
           //[
            // {
            // id: 'xxx',
            // name: '超级管理员',
            // shortname: 'super_admin',
            // fileAuth: {
            //     [guid]: [
            //         {
            //             name: 'xxx',
            //             target: {
            //                 parent: '',
            //                 parentSub: '',
            //                 all: false,
            //                 deep: false
            //             },
            //             auth: {
            //                 read: {
            //                     value: false,
            //                     freeze: ''
            //                 },
            //                 add: {
            //                     value: false,
            //                     freeze: ''
            //                 },
            //                 delete: {
            //                     value: false,
            //                     freeze: ''
            //                 }
            //             }
            //         }
            //     ]
            // }
            // }
            //],
        // YArray | () => YArray
        users: new YArray()
        // [
            /** 
             * @example
             * { 
             *  id: 'xxxx',
             *  email: '',
             *  nickname: '',
             *  roles: [ 'super_admin', 'admin' ]
             * }
             */
        // ]
    }
}

/**
 * 
 * @param {{
*  [key: string] : any
* } | null | undefined} authConfig 
*/
export const combineDocAuthConfig = (authConfig, anotherAuthConfig = getDefaultDocAuthConfig()) => {
   if(!authConfig){
       return anotherAuthConfig
   }
   const keys = Object.keys(anotherAuthConfig)
   keys.forEach(key => {
       const defaultAuthConfigValue = anotherAuthConfig[key]
       const authConfigValue = authConfig[key]
       if(typeof authConfigValue === 'undefined'){
           authConfig[key] = anotherAuthConfig[key]
       } else {
           if(isPlainObject(defaultAuthConfigValue) && isPlainObject(authConfigValue)){
            authConfig[key] = combineDocAuthConfig(authConfig[key], anotherAuthConfig[key])
           }
       }
   })
   return authConfig
}

/**
 * 
 * @param {Item} item 
 * @param {Transaction} transaction 
 * @param {string} currentUser
 * @returns {YMap<any> | null}
 */
export const getUserInfo = (item, transaction, currentUser = '') => {
     // @ts-ignore
     const authConfig = item.parent.doc.auth
     // @ts-ignore
     const signatureConfig = item.parent.doc.signature
     /**
      * @type {YArray<YMap<any>>}
      */
     const users = typeof authConfig.users === 'function' ? authConfig.users(transaction.doc) : authConfig.users
     const curretUser = currentUser || authConfig.currentUser || signatureConfig.currentUser
     const userInfoIndex = users.toArray().findIndex(user => 
       user.get('email') === curretUser)
     if(userInfoIndex < -1){
       // 检查失败，用户信息不存在
       debugLog('checkAddAuth')
       item.hide(transaction, true)
       return null
     }
     return users.get(userInfoIndex)
}

/**
 * 
 * @param {Item} item 
 * @param {Transaction} transaction
 * @param {string} currentUser
 * @returns {YArray<any> | null}
 */
export const getUserRoles = (item, transaction, currentUser) => {
    const userInfo = getUserInfo(item, transaction, currentUser)
    if(!userInfo) return null
    return userInfo.get('roles')
}

/**
 * 
 * @param {Item} item 
 * @param {Transaction} transaction 
 * @param {string} currentUser
 * @returns {Array<any>}
 */
export const getUserRoleConfig = (item, transaction, currentUser) => {
    const userRoles = getUserRoles(item, transaction, currentUser)?.toJSON() ?? []
    // @ts-ignore
    const authConfig = item.parent.doc.auth
    /**
     * @type {YArray<YMap<any>>}
     */
    const allRoles = typeof authConfig.roles === 'function' ? authConfig.roles(transaction.doc) : authConfig.roles
    const roleConfigs = allRoles.toArray().filter(role => {
        return userRoles.includes(role.get('shortname'))
    })
    return roleConfigs
}

/**
 * 
 * @param {Item} item 
 * @param {Transaction} transaction 
 * @param {string} currentUser
 * @returns {Array<YMap<any>>}
 */
export const getUserRoleRulesInDoc = (item, transaction, currentUser) => {
    const userRoleConfigs = getUserRoleConfig(item, transaction, currentUser)
    /**
     * @type {Array<YMap<any>>}
     */
    const rules = []
    for(let userRoleConfig of userRoleConfigs){
        const fileAuth = userRoleConfig.get('fileAuth')
        if(!fileAuth){
            debugLog('getRoleRulesInDoc - 获取fileAuth失败', item, transaction)
            continue
        }
        /**
         * @type {YArray<YMap<any>>}
         */
        // @ts-ignore
        const rulesOfDoc = fileAuth.get(item.parent.doc.guid)
        rulesOfDoc.forEach((rule) => {
            rules.push(rule)
        })
    }
    return rules
}

/**
 * 
 * @param {Item} item 
 * @param {YMap<any>} rule 
 * @returns {boolean}
 */
const _isItemMeetRuleParent = (item, rule) => {
    const target = rule.get('target')
    const parent = target.get('parent')
    const parentSub = target.get('parentSub')
    const parentSignal = item.getParentSignal()
    if((typeof parentSignal === 'string' || (parentSignal instanceof String))
    && (typeof parent === 'string' || (parent instanceof String))){
        if(parentSignal !== parent) {
          return false
        }
    } else if(parentSignal instanceof Array && parent instanceof Array){
        if(parentSignal[0] !== parent[0] || parentSignal[1] !== parent[1]) {
          return false
        }
    } else {
      return false
    }

    return parentSub === item.parentSub
}

/**
 * 
 * @param {Item} item 
 * @param {YMap<any>} rule 
 * @returns {boolean}
 */
const _isItemMeetRuleParentDeep = (item, rule) => {
    if(!item) return false
    if(_isItemMeetRuleParent(item, rule)) return true
    // @ts-ignore
    const parentItem = item.parent ? item.parent._item : null
    return _isItemMeetRuleParentDeep(parentItem, rule)
}

/**
 * 
 * @param {Item} item 
 * @param {YMap<any>} rule 
 * @param {boolean} deep
 * @returns {boolean}
 */
export const isItemMeetRuleParent = (item, rule, deep) => {
    if(deep) return _isItemMeetRuleParentDeep(item, rule)
    else return _isItemMeetRuleParent(item, rule)
}

/**
 * 
 * @param {Item} item 
 * @param {Transaction} transaction
 * @param {string} currentUser
 * @returns {Array<any>}
 */
export const getMetRulesOfItem = (item, transaction, currentUser) => {
    const rules = getUserRoleRulesInDoc(item, transaction, currentUser)
    const metRules = []
    for(let rule of rules){
        /**
         * @type {YMap<any>}
         */
        const target = rule.get('target')
        if(target.get('all')){
            metRules.push(rule)
        } else if(isItemMeetRuleParent(item, rule, target.get('deep'))){
            metRules.push(rule)
        }
    }
    return metRules
}

/**
 * 检查currentUser的read权限
 * @param {Item} item 
 * @param {Transaction} transaction 
 * @returns {boolean}
 */
export const checkReadAuth = (item, transaction) => {
    const authConfig = transaction.doc.auth
    // 如果没有开启权限校验，就认为权限校验通过
    if(!authConfig.check || !transaction.doc.auth.checkSource[item.source]){
        item.hide(transaction, false)
        return true
    }

    // 检查当前用户的可读权限
    const currentUserRules = getMetRulesOfItem(item, transaction, transaction.doc.auth.currentUser || transaction.doc.signature.currentUser)
    for(let rule of currentUserRules){
        const auth = rule.get('auth').toJSON()
        const { read: readOp } = auth
        // 只要有一个规则规定currentUser可以看，那么就校验通过
        if(readOp.value){
            item.hide(transaction, false)
            return true
        }
    }
    return false
}

/**
 * 检查currentUser的add权限
 * @param {Item} item 
 * @param {Transaction} transaction
 */
export const checkAddAuth = (item, transaction) => {
    const authConfig = transaction.doc.auth
    // 如果没有开启权限校验，就认为权限校验通过
    if(!authConfig.check){
        item.hide(transaction, false)
        return
    }

    if(!transaction.doc.auth.checkSource[item.source]){
        item.hide(transaction, false)
        return
    }

    // 已经被删除，那么不需要检查add权限
    if(item.deleted){
        return
    }

    // 如果有集成签名并且集成签名校验通过
    if(item.integrateSignature && item.integrateSignature.verify === 1){
        const signatory = item.integrateSignature.signatory
        const signatoryUserRules = getMetRulesOfItem(item, transaction, signatory)
        for(let rule of signatoryUserRules){
            const auth = rule.get('auth').toJSON()
            const { add: addOp } = auth
            if(checkOpPermission(item, -1, addOp)){
                item.hide(transaction, false)
                return
            }
        }
        item.hide(transaction, true)
        return
    }

    // 如果只有原子签名
    if(item.signature){
        const signature = [ ...item.signature ]
        for(let index = 0 ; index < signature.length ; index ++){
            const signatureItem = signature[index]
            if(signatureItem.verify === 1){
                const signatory = signatureItem.signatory
                const signatoryUserRules = getMetRulesOfItem(item, transaction, signatory)
                for(let rule of signatoryUserRules){
                    const auth = rule.get('auth').toJSON()
                    const { add: addOp } = auth
                    if(checkOpPermission(item, index, addOp)){
                        item.hide(transaction, false, index)
                    }
                }
            } else {
                item.hide(transaction, true, index)
            }  
        }
        return
    }

    item.hide(transaction, true)
}

/**
 * 检查currentUser的add权限
 * @param {Item} item 
 * @param {Transaction} transaction
 */
export const checkDeleteAuth = (item, transaction) => {
    const authConfig = transaction.doc.auth
    // 如果没有开启权限校验，就认为权限校验通过
    if(!authConfig.check || !transaction.doc.auth.checkSource[getSourceFromTransaction(transaction)]){
        if(transaction.checkPriority.delete){
            item.deletePhase(transaction)
        }
        return
    }

    if(item.deleteSignature){
        const deleteSignature = [ ...item.deleteSignature ]
        /**
         * @type {number}
         */
        let firstFialedIndex = -1
        for(let index = 0 ; index < deleteSignature.length ; index ++){
            const signatureItem = deleteSignature[index]
            // 删除行为没有签名，删除被拒绝
            if(!signatureItem) continue
            const signatory = signatureItem.signatory
            const signatoryUserRules = getMetRulesOfItem(item, transaction, signatory)
            /**
             * @type {boolean}
             */
            let verifyresult = true
            for(let rule of signatoryUserRules){
                const auth = rule.get('auth').toJSON()
                if(!checkOpPermission(item, index, auth.delete)){
                    verifyresult = false
                    break
                }
            }

            if(!verifyresult){
                firstFialedIndex = index
                break
            }
        }


        if(firstFialedIndex !== -1){
            // 验证失败
            if(firstFialedIndex === 0){
                if(item.length > 1) {
                    checkDeleteAuth(getItemCleanStart(transaction, createID(item.id.client, item.id.clock + 1)), transaction)
                }

                if(!transaction.checkPriority.delete){
                    item.unDelete(transaction)
                }
            } else if(firstFialedIndex > 0){
                const len = item.length
                const failedItem = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + firstFialedIndex))
                if(firstFialedIndex < len - 1){
                    checkDeleteAuth(getItemCleanStart(transaction, createID(item.id.client, item.id.clock + firstFialedIndex + 1)), transaction)
                }

                if(transaction.checkPriority.delete){
                    item.deletePhase(transaction)
                } else {
                    failedItem.unDelete(transaction)
                }
            }
        } else {
            if(transaction.checkPriority.delete){
                item.deletePhase(transaction)
            }
        }
    } else {
        if(!transaction.checkPriority.delete){
            item.unDelete(transaction)
        }
    }
}

/**
 * 
 * @param {Item} item 
 * @param {number} offset
 * @param {{
 *   [key: string]: any
 * }} op
 * @returns {boolean}
 */
const checkOpPermission = (item, offset, op) => {
    if(op.value){
        return true
    }
    if(!op.freeze){
        return false
    }
    if(offset === -1){
        for(let clock = item.id.clock ; clock < item.id.clock + item.length ; clock ++){
            if(!op.freeze.includes(JSON.stringify([item.id.client, clock]))){
                return false
            }
        }
        return true
    } else {
        if(op.freeze.includes(JSON.stringify([item.id.client, item.id.clock + offset]))){
            return true
        }
    }
    return false
}