import { decoding, encoding } from "lib0"
import { Item, MerkleTreeLeafNode, MerkleTreeLeafNodeList, MerkleTreeLayerNodeList, getItemSignal, Transaction, ID, MerkleTreeLayerNode, MerkleTreeHashNode, getAllAtomicItemHash, createID, compareIDs, compareIDsByNameMode, Doc, getState, findIndexSS, UpdateEncoderV1, readStructFromDecoder, UpdateDecoderV1, GC } from "../internals"
import crypto from 'crypto'

export const LEAF_NODE_LAYER = -1
export const STRUCT_LAYER = -2
export const CONFLICT_RECONCILE_LAYER = -3

/**
 * 添加Merkle Tree Leaf Node
 * @param {ByzantineStore} store
 * @param {number} client
 *
 * @function
 */
export const initialByzantineClientStore = (store, client) => {
    const merkleTreeLeafNodeList = new MerkleTreeLeafNodeList()
    const merkleTreeLayerNodeList = new MerkleTreeLayerNodeList()
    const byzantineClientStore = {
        leafNodeList: merkleTreeLeafNodeList,
        layerNodeList: merkleTreeLayerNodeList,
        merkleTreeRoot: null,
        hashTable: new Map()
    }
    store.clients.set(client, byzantineClientStore)
    return byzantineClientStore
}

/**
 * 计算一个叶子节点的扇hash
 * @param {MerkleTreeLeafNode} leafNode 
 * @param {{
 *   leafNodeList: MerkleTreeLeafNodeList,
 *   layerNodeList: MerkleTreeLayerNodeList,
 *   merkleTreeRoot: MerkleTreeHashNode | null | undefined,
 * }} clientByzantineStore
 * @returns {string}
 */
export const createFHash = (leafNode, clientByzantineStore) => {
    const leafNodeList = clientByzantineStore.leafNodeList
    const layerNodeList = clientByzantineStore.layerNodeList
    /**
     * 当前节点在第几层，-1表示是叶子节点
     * @type {number}
    */
    let layer = -1
    /**
     * 当前节点的顺序位置，从1开始
     * @type {number}
     */
    let index = leafNodeList.arr.findIndex(_leafNode => _leafNode === leafNode) + 1
    /**
     * @type {string}
     */
    let fhashTarget = ''
    while(index){
        // 如果是奇数
        if(index % 2 === 1){
            if(layer === -1){
                fhashTarget += leafNodeList.arr[index - 1].hash
            } else {
                fhashTarget += layerNodeList.arr[layer].arr[index - 1].hash
            }
        }
        index = Math.round(index / 2)
        layer ++
    }
    return crypto.createHash("sha256").update(fhashTarget).digest('base64')
}

/**
 * 获取一个叶子节点的所有FHash节点
 * @param {MerkleTreeLeafNode} leafNode 
 * @param {{
 *   leafNodeList: MerkleTreeLeafNodeList,
 *   layerNodeList: MerkleTreeLayerNodeList,
 *   merkleTreeRoot: MerkleTreeHashNode | null | undefined,
 * }} clientByzantineStore
 * @returns {Array<{
 *   layer: number,
 *   offset: number,
 *   hash: string,
 *   item: MerkleTreeLeafNode | MerkleTreeHashNode
 * }>}
*/
export const getFHashNodes = (leafNode, clientByzantineStore) => {
    const leafNodeList = clientByzantineStore.leafNodeList
    const layerNodeList = clientByzantineStore.layerNodeList
    /**
     * 当前节点在第几层，-1表示是叶子节点
     * @type {number}
    */
    let layer = -1
    /**
     * 当前节点的顺序位置，从1开始
     * @type {number}
     */
    let index = leafNode.clock + 1
    /**
     * @type {Array<{
     *   layer: number,
     *   offset: number,
     *   hash: string,
     *   item: MerkleTreeLeafNode | MerkleTreeHashNode
     * }>}
     */
    let fhashNodes = []
    while(index){
        // 如果是奇数
        if(index % 2 === 1){
            /**
             * @type {MerkleTreeLeafNode | MerkleTreeHashNode}
             */
            let fhashNode
            if(layer === -1){
                fhashNode = leafNodeList.arr[index - 1]
            } else {
                fhashNode = layerNodeList.arr[layer].arr[index - 1]
            }
            fhashNodes.push({
                layer,
                offset: index - 1,
                hash: fhashNode.hash ?? '',
                item: fhashNode
            })
        }
        index = Math.round(index / 2)
        layer ++
    }
    return fhashNodes
}

/**
 * @param {Map<number, Array<MerkleTreeLeafNode>>} leafNodesMap
 * @param {ByzantineStore} store 
 */
export const addLeafNodesToMerkleTree = (leafNodesMap, store) => {
    const clients = leafNodesMap.keys()
    for(let client of clients){
        // 如果没有该client的拜占庭store，则初始化一个
        let byzantineClientStore = store.clients.get(client)
        if (byzantineClientStore === undefined) {
            byzantineClientStore = initialByzantineClientStore(store, client)
        }

        // 获取其层节点列表
        const layerNodeList = byzantineClientStore.layerNodeList
        // 获取其需要加入树的叶子结点列表
        const clientLeafNodes = leafNodesMap.get(client)
        // 如果没有列表，则直接跳过
        if(!clientLeafNodes || clientLeafNodes.length === 0) continue
        /**
         * 当前需要加入树的节点列表
         * @type {Array<MerkleTreeHashNode | MerkleTreeLeafNode>}
        */
        let hashNodeQueue = clientLeafNodes
        /**
         * 下一层需要加入树的节点列表
        * @type {Array<MerkleTreeHashNode | MerkleTreeLeafNode>}
        */
        let nextHashNodeQueue = []
        // 如果没有任何层，则初始化一层
        if(layerNodeList.length === 0 && hashNodeQueue.length){
            const newLayerNode = new MerkleTreeLayerNode()
            layerNodeList.arr.push(newLayerNode)
        }
        // 设置该层的最后一个hash节点
        let lastHashNode

        for(let layer = 0; layer < layerNodeList.length ; layer ++){
            // 如果节点列表为空，则认为这层已经没有需要加入的新节点了，结束
            if(!hashNodeQueue || hashNodeQueue.length === 0) break
            /**
             * 获取该层的hash节点列表
             * @type {MerkleTreeLayerNode}
             */
            let layerNode = layerNodeList.arr[layer]
            // 如果该层节点不存在
            if(!layerNode){
                // 初始化该层
                layerNode = new MerkleTreeLayerNode()
                // 设置该层节点
                layerNodeList.arr[layer] = layerNode
            }
            
            // 如果该层节点为空，则初始化
            if(layerNode.length === 0){
                lastHashNode = new MerkleTreeHashNode()
                // 层节点加入该节点
                layerNode.arr.push(lastHashNode)
                // 下一层需要处理该节点
                nextHashNodeQueue.push(lastHashNode)
            } else {
                // 直接获取最后一个节点
                lastHashNode = layerNode.arr[layerNode.length - 1]
            }
            
            /**
             * 从待加入树的节点中获取指定位置的节点
             * @type {MerkleTreeLeafNode | MerkleTreeHashNode | null | undefined}
             */
            let leafNode = hashNodeQueue.shift()
            while(leafNode){
                if(leafNode.parent){
                    // 如果已经名花有主，那么就换一个
                    leafNode = hashNodeQueue.shift()
                    continue
                }

                // hash节点已完全, 创建新的hash节点
                if(lastHashNode.left && lastHashNode.right){
                    lastHashNode = new MerkleTreeHashNode()
                    layerNode.arr.push(lastHashNode)
                    nextHashNodeQueue.push(lastHashNode)
                }

                if(!lastHashNode.left){
                    // 左孩子没有，就加左孩子
                    lastHashNode.left = leafNode
                } else if(!lastHashNode.right){
                    // 否则右孩子没有，就加右孩子
                    lastHashNode.right = leafNode
                }

                // 如果还有剩下的没有加入的节点，就继续循环
                leafNode = hashNodeQueue.shift()
            }
                 
            // 如果已经到达顶层，但是还有没有加入的节点，那么就创建新层，继续遍历
            // 如果该层的游离节点的数量为1并且游离节点数量 === 层节点数量，不再创建新层
            if(layer === layerNodeList.length - 1 
                && !(
                    layerNode.arr.length === nextHashNodeQueue.length 
                    && nextHashNodeQueue.length === 1
                )){
                // 如果层已经到头，但是还有新的hashNode需要合并
                const newLayerNode = new MerkleTreeLayerNode()
                layerNodeList.arr.push(newLayerNode)
            }

            // 切换待加入的节点列表
            hashNodeQueue = nextHashNodeQueue
            nextHashNodeQueue = []
        }

        // 设置merkletree root节点，每一次数据同步时需要传递该节点确定历史是否一致
        byzantineClientStore.merkleTreeRoot = lastHashNode
    }
}

/**
 * 在TX结束时进行Merkle Tree增量构建
 * @param {Transaction} transaction
 * @param {ID} structId
 * @param {Array<MerkleTreeLeafNode>} leafNodes 
 */
export const addPendingMerkleTreeLeafNodesToTransaction = (transaction, structId ,leafNodes) => {
    let pendingMerkleTreeLeafNodes = transaction.pendingMerkleTreeLeafNodes.get(structId.client)
    if(!pendingMerkleTreeLeafNodes){
        pendingMerkleTreeLeafNodes = []
        transaction.pendingMerkleTreeLeafNodes.set(structId.client, pendingMerkleTreeLeafNodes)
    }
    pendingMerkleTreeLeafNodes.push(...leafNodes)
}

/**
 * 在store中查询叶子节点，如果查询成功返回叶子节点，否则返回null
 * @param {ByzantineStore} store 
 * @param {Item} struct
 * @param {ID} id
 * @returns {MerkleTreeLeafNode | null}
 */
export const findMerkleTreeLeafNodeInStore = (store, struct, id) => {
    const leafNodeList = store.clients.get(struct.id.client)?.leafNodeList
    if(leafNodeList){
        if(leafNodeList.arr.length - 1 >= id.clock){
            return leafNodeList.arr[id.clock]
        }
    }
    return null
}

/**
 * 添加Merkle Tree Leaf Node
 * @param {ByzantineStore} store
 * @param {Item} struct
 * @param {Transaction} transaction
 * @returns {Array<MerkleTreeLeafNode>}
 *
 * @function
 */
export const addMerkleTreeLeafNodes = (store, struct, transaction) => {
    let byzantineClientStore = store.clients.get(struct.id.client)
    if (byzantineClientStore === undefined) {
        byzantineClientStore = initialByzantineClientStore(store, struct.id.client)
    }
    const len = struct.length
    const leafNodeList = []
    for(let i = 0 ; i < len ; i ++){
        const id = createID(struct.id.client, struct.id.clock + i)
        // 查询叶子节点是否存在，叶子节点的id是一样的
        let isExist = findMerkleTreeLeafNodeInStore(store, struct, id)
        if(!isExist){
            isExist = new MerkleTreeLeafNode(id, [{ struct }], byzantineClientStore)
            byzantineClientStore.leafNodeList.add(isExist)
            // 节点被新增了，需要建立默克尔树
            addPendingMerkleTreeLeafNodesToTransaction(transaction, struct.id, [ isExist ])
        } else {
            isExist.addTarget({ struct })
        }
        leafNodeList.push(isExist)
    }
    return leafNodeList
}

/**
 * 
 * @param {Doc} doc 
 */
export const readByzantineState = (doc) => {
    const byzantineStore = doc.byzantineStore
    const clients = byzantineStore.clients
    /**
     * @type {Map<number, {
     *    state: number,
     *    fhash: string
     * }>}
     */
    const byzantineInfo = new Map()
    for(let [client, clientStore] of clients.entries()){
        const len = clientStore.leafNodeList.arr.length
        const leafNode = clientStore.leafNodeList.arr[len - 1]
        const state = leafNode.clock
        const fhash = leafNode.fHash
        byzantineInfo.set(client, { state, fhash })
    }
    return byzantineInfo
}

/**
 * 将doc的byzatine的状态数据写入encoder中，返回true表示写入有数据，否则表示没有写入有效数据
 * @param {encoding.Encoder} encoder
 * @param {Doc} doc
 */
export const writeByzantineStateToEncoder = (encoder, doc) => {
    const docFHashs = readByzantineState(doc)
    encoding.writeVarUint(encoder, docFHashs.size)
    for(let [client, byzantineInfo] of docFHashs.entries()){
        encoding.writeVarUint(encoder, client)
        encoding.writeVarUint(encoder, byzantineInfo.state)
        encoding.writeVarString(encoder, byzantineInfo.fhash)
    }
    return docFHashs.size > 0
}

/**
 * 
 * @param {decoding.Decoder} decoder 
 */
export const readByzantineStateFromDecoder = (decoder) => {
    /**
     * @type {Map<number, {
     *    state: number,
     *    fhash: string
     * }>}
     */
    const fhashs = new Map()
    const len = decoding.readVarUint(decoder)
    for(let i = 0 ; i < len ; i ++){
        const client = decoding.readVarUint(decoder)
        const state = decoding.readVarUint(decoder)
        const fhash = decoding.readVarString(decoder)
        fhashs.set(client, { state, fhash })
    }
    return fhashs
}

/**
 * 获取byzantineStates所记录的拜占庭状态的所有的fhash计算构成的节点
 * @param {Map<number, {
 *    state: number,
 *    fhash: string
 * }>} byzantineStates 
 * @param {Doc} doc 
 * @returns {Map<number, Array<{
 *   layer: number,
 *   offset: number,
 *   hash: string,
 *   item: MerkleTreeLeafNode | MerkleTreeHashNode
 * }>>
 * }
 */
export const getFanNodesFromByzantineStates = (byzantineStates, doc) => {
    const byzantineStore = doc.byzantineStore
    /**
     * @type {Map<number, Array<{
     *   layer: number,
     *   offset: number,
     *   hash: string,
     *   item: MerkleTreeLeafNode | MerkleTreeHashNode
     * }>>
     * }
     */
    const fanNodesMap = new Map()
    for(let [client, { state }] of byzantineStates){
        const clientByzantineStore = byzantineStore.clients.get(client)
        if(clientByzantineStore){
            const leafNode = clientByzantineStore.leafNodeList.arr[state]
            const fHashNodes = getFHashNodes(leafNode, clientByzantineStore)
            fanNodesMap.set(client, fHashNodes)
        }
    }
    return fanNodesMap
}


/**
 * 将doc的byzatine的状态数据写入encoder中，返回true表示写入有数据，否则表示没有写入有效数据
 * @param {encoding.Encoder} encoder
 * @param {Map<number, Array<{
 * layer: number;
 * offset: number;
 * hash?: string;
 * hashs?: Array<string>,
 * item?: MerkleTreeLeafNode | MerkleTreeHashNode,
 * localMissing?: Array<Item|GC>,
 * remoteMissing?: Array<string>
 * }>>} fanNodes
 * @param {Doc} doc
 * @returns {boolean}
 */
export const writeFanNodesToEncoder = (encoder, fanNodes, doc) => {
    encoding.writeVarUint(encoder, fanNodes.size)
    for(let [client, fNodes] of fanNodes){
        encoding.writeVarUint(encoder, client)
        encoding.writeVarUint(encoder, fNodes.length)
        for(let fNode of fNodes){
            const layer = fNode.layer
            encoding.writeVarInt(encoder, layer)
            encoding.writeVarUint(encoder, fNode.offset)
            if(layer >= LEAF_NODE_LAYER){
                if(fNode.hash){
                    encoding.writeVarString(encoder, fNode.hash)
                }
            } else if(layer === STRUCT_LAYER){
                if(fNode.hashs){
                    encoding.writeVarUint(encoder, fNode.hashs.length)
                    for(let hash of fNode.hashs){
                        encoding.writeVarString(encoder, hash)
                    }
                }
            } else if(layer === CONFLICT_RECONCILE_LAYER){
                const localMissing = fNode.localMissing
                const remoteMissing = fNode.remoteMissing
                if(localMissing && remoteMissing){
                    encoding.writeVarUint(encoder, localMissing.length)
                    const itemEncoder = new UpdateEncoderV1()
                    for(let localMissingItem of localMissing){
                        encoding.writeVarUint(itemEncoder.restEncoder, localMissingItem.id.clock)
                        localMissingItem.write(itemEncoder, 0)
                    }
                    encoding.writeVarUint8Array(encoder, itemEncoder.toUint8Array())

                    encoding.writeVarUint(encoder, remoteMissing.length)
                    for(let hash of remoteMissing){
                        encoding.writeVarString(encoder, hash)
                    }
                }
            }
        }
    }
    return true
}

/**
 * 
 * @param {decoding.Decoder} decoder 
 * @param {Doc} doc
 * @param {Transaction | null} transaction
 * @returns {Map<number, Array<{
 * layer: number;
 * offset: number;
 * hash?: string;
 * hashs?: Array<string>,
 * item?: MerkleTreeLeafNode | MerkleTreeHashNode,
 * localMissing?: Array<Item|GC>,
 * remoteMissing?: Array<string>
 * }>>}
 */
export const readFanNodesFromDecoder = (decoder, doc, transaction = null) => {
    /**
     * @type {Map<number, Array<{
     * layer: number;
     * offset: number;
     * hash?: string;
     * hashs?: Array<string>,
     * item?: MerkleTreeLeafNode | MerkleTreeHashNode,
     * localMissing?: Array<Item|GC>,
     * remoteMissing?: Array<string>
     * }>>}
     */
    const fanNodesMap = new Map()
    const len = decoding.readVarUint(decoder)
    for(let i = 0 ; i < len ; i ++){
        const client = decoding.readVarUint(decoder)
        const fNodesLen = decoding.readVarUint(decoder)
        /**
         * @type {Array<{
         * layer: number;
         * offset: number;
         * hash?: string;
         * hashs?: Array<string>,
         * item?: MerkleTreeLeafNode | MerkleTreeHashNode,
         * localMissing?: Array<Item|GC>,
         * remoteMissing?: Array<string>
         * }>}
         */
        const fNodes = []
        for(let j = 0 ; j < fNodesLen ; j ++){
            const layer = decoding.readVarInt(decoder)
            const offset = decoding.readVarUint(decoder)
            const item = getFanNodeByPosition(doc.byzantineStore, client, layer, offset)
            if(layer >= LEAF_NODE_LAYER){
                const hash = decoding.readVarString(decoder)
                fNodes.push({ layer, offset, hash, item })
            } else if(layer === STRUCT_LAYER){
                const hashsLen = decoding.readVarUint(decoder)
                /**
                 * @type {Array<string>}
                 */
                const hashs = []
                for(let i = 0 ; i < hashsLen; i ++){
                    const hash = decoding.readVarString(decoder)
                    hashs.push(hash)
                }
                fNodes.push({ layer, offset, hashs, item })
            } else if(layer === CONFLICT_RECONCILE_LAYER){
                const localMissingLen = decoding.readVarUint(decoder)
                /**
                 * @type {Array<Item|GC>}
                 */
                const localMissing = []
                const itemDecoder = new UpdateDecoderV1(new decoding.Decoder(decoding.readVarUint8Array(decoder)))
                for(let i = 0 ; i < localMissingLen; i ++){
                    /**
                     * @type {Array<Item|GC>}
                     */
                    localMissing.push(readStructFromDecoder(itemDecoder, doc, transaction, 1, [], client)[0])
                }

                const remoteMissingLen = decoding.readVarUint(decoder)
                /**
                 * @type {Array<string>}
                 */
                const remoteMissing = []
                for(let i = 0 ; i < remoteMissingLen ; i ++){
                    const hash = decoding.readVarString(decoder)
                    remoteMissing.push(hash)
                }
                fNodes.push({ layer, offset, localMissing, remoteMissing, item })
            }
        }
        fanNodesMap.set(client, fNodes)
    }
    return fanNodesMap
}

/**
 * 
 * @param {ByzantineStore} byzantineStore 
 * @param {number} client 
 * @param {number} layer 
 * @param {number} offset
 * @returns {MerkleTreeLeafNode | MerkleTreeHashNode | undefined}
 */
export const getFanNodeByPosition = (byzantineStore, client, layer, offset) => {
    const clientByzantineStore = byzantineStore.clients.get(client)
    if(!clientByzantineStore) return
    if(layer <= -1){
        const leafNodeList = clientByzantineStore.leafNodeList.arr
        if(offset >= leafNodeList.length) return
        return leafNodeList[offset]
    } else {
        const layerNodeList = clientByzantineStore.layerNodeList.arr
        if(layer >= layerNodeList.length) return
        const layerNode = layerNodeList[layer].arr
        if(offset >= layerNode.length) return
        return layerNode[offset]
    }
}

/**
 * 获取指定id的原子item的fhash
 * @param {ID} id 
 * @param {Doc} doc
 * @returns {null | string}
 */
export const getStructFHashByID = (id, doc) => {
    const clientByzantineStore = doc.byzantineStore.clients.get(id.client)
    let leafNodeList
    if(clientByzantineStore && id.clock < (leafNodeList = clientByzantineStore.leafNodeList.arr).length){
        const leafNode = leafNodeList[id.clock]
        return leafNode.fHash
    } else return null
}

/**
 * 
 * @param {Map<number, {
 *   state: number,
 *   fhash: string
 * }>} remoteByzantineState 
 * @param {Map<number, {
 *   state: number,
 *   fhash: string
 * }>} localByzantineState
 * @returns {Array<number>}
 */
export const diffByzantineStates = (remoteByzantineState, localByzantineState) => {
        // 1.取client交集
        const remoteClients = remoteByzantineState.keys()
        const commonClients = []
        for(let remoteClient of remoteClients){
            if(localByzantineState.has(remoteClient)
            // @ts-ignore
            && localByzantineState.get(remoteClient).state >= remoteByzantineState.get(remoteClient).state){
                commonClients.push(remoteClient)
            }
        }
        return commonClients
}


export class ByzantineStore {
    constructor () {
        /**
         * @type {Map<number,{
         *   leafNodeList: MerkleTreeLeafNodeList,
         *   layerNodeList: MerkleTreeLayerNodeList,
         *   merkleTreeRoot: MerkleTreeHashNode | null | undefined,
         *   hashTable: Map<string, Item>
         * }>}
         */
        this.clients = new Map()

        /**
         * 记录所有当前由于各种原因无法引入历史中的冲突structs
         * @type {null | { structs: Map<number, Array<Item | GC>> }}
         */
        this.pendingStructs = null
      }
}