/**
 * @file Command.ts
 * @description 思维导图命令管理模块，负责处理命令执行、历史记录和数据变更
 * @module mindmap/core/command
 */

import {
  copyRenderTree,
  simpleDeepClone,
  throttle,
  isSameObject,
  transformTreeDataToObject
} from '../../utils'
import { ERROR_TYPES } from '../../constants/constant'
import pkg from '../../../package.json'

/**
 * 命令管理类
 * @class Command
 * @description 实现命令模式,管理思维导图的所有操作命令
 * 主要功能:
 * 1. 命令注册与执行
 * 2. 操作历史管理(撤销/重做)
 * 3. 数据变更追踪
 */
class Command {
  private opt: Record<string, any>
  private mindMap: any
  private commands: Record<string, Function[]>
  public history: any[]
  private activeHistoryIndex: number
  public originAddHistory: Function
  private isPause: boolean

  /**
   * @param opt 配置选项
   */
  constructor(opt: Record<string, any> = {}) {
    this.opt = opt
    this.mindMap = opt.mindMap
    this.commands = {}
    this.history = []
    this.activeHistoryIndex = 0
    // 注册快捷键
    this.registerShortcutKeys()
    this.originAddHistory = this.addHistory.bind(this)
    // 节流处理历史记录添加
    this.addHistory = throttle(
      this.addHistory,
      this.mindMap.opt.addHistoryTime,
      this
    )
    // 是否暂停收集历史数据
    this.isPause = false
  }

  /**
   * 暂停收集历史数据
   */
  pause(): void {
    this.isPause = true
  }

  /**
   * 恢复收集历史数据
   */
  recovery(): void {
    this.isPause = false
  }

  /**
   * 清空历史数据
   */
  clearHistory(): void {
    this.history = []
    this.activeHistoryIndex = 0
    this.mindMap.emit('back_forward', 0, 0)
  }

  /**
   * 注册快捷键
   * @private
   */
  private registerShortcutKeys(): void {
    this.mindMap.keyCommand.addShortcut('Control+z', () => {
      this.mindMap.execCommand('BACK')
    })
    this.mindMap.keyCommand.addShortcut('Control+y', () => {
      this.mindMap.execCommand('FORWARD')
    })
  }

  /**
   * 执行命令
   * @param name 命令名称
   * @param args 命令参数
   */
  exec(name: string, ...args: any[]): void {
    if (this.commands[name]) {
      this.commands[name].forEach(fn => {
        fn(...args)
      })
      this.mindMap.emit('afterExecCommand', name, ...args)
      if (
        ['BACK', 'FORWARD', 'SET_NODE_ACTIVE', 'CLEAR_ACTIVE_NODE'].includes(
          name
        )
      ) {
        return
      }
      this.addHistory()
    }
  }

  /**
   * 添加命令
   * @param name 命令名称
   * @param fn 命令执行函数
   */
  add(name: string, fn: Function): void {
    if (this.commands[name]) {
      this.commands[name].push(fn)
    } else {
      this.commands[name] = [fn]
    }
  }

  /**
   * 移除命令
   * @param name 命令名称
   * @param fn 要移除的具体函数,不传则移除该命令的所有函数
   */
  remove(name: string, fn?: Function): void {
    if (!this.commands[name]) {
      return
    }
    if (!fn) {
      delete this.commands[name]
    } else {
      const index = this.commands[name].findIndex(item => item === fn)
      if (index !== -1) {
        this.commands[name].splice(index, 1)
      }
    }
  }

  /**
   * 添加历史记录
   * @private
   */
  public addHistory(): void {
    if (this.mindMap.opt.readonly || this.isPause) {
      return
    }
    const lastData = this.history.length > 0 ? this.history[this.activeHistoryIndex] : null
    const data = this.getCopyData()
    // 避免重复添加相同数据
    if (lastData === data) return
    if (lastData && JSON.stringify(lastData) === JSON.stringify(data)) {
      return
    }
    this.emitDataUpdatesEvent(lastData, data)
    // 删除当前历史指针后面的数据
    this.history = this.history.slice(0, this.activeHistoryIndex + 1)
    this.history.push(simpleDeepClone(data))
    // 限制历史记录数量
    if (this.history.length > this.mindMap.opt.maxHistoryCount) {
      this.history.shift()
    }
    this.activeHistoryIndex = this.history.length - 1
    this.mindMap.emit('data_change', data)
    this.mindMap.emit(
      'back_forward',
      this.activeHistoryIndex,
      this.history.length
    )
  }

  /**
   * 撤销操作
   * @param step 撤销步数
   * @returns 撤销后的数据
   */
  back(step: number = 1): any {
    if (this.mindMap.opt.readonly) {
      return
    }
    if (this.activeHistoryIndex - step >= 0) {
      const lastData = this.history[this.activeHistoryIndex]
      this.activeHistoryIndex -= step
      this.mindMap.emit(
        'back_forward',
        this.activeHistoryIndex,
        this.history.length
      )
      const data = simpleDeepClone(this.history[this.activeHistoryIndex])
      this.emitDataUpdatesEvent(lastData, data)
      return data
    }
  }

  /**
   * 重做操作
   * @param step 前进步数
   * @returns 重做后的数据
   */
  forward(step: number = 1): any {
    if (this.mindMap.opt.readonly) {
      return
    }
    const len = this.history.length
    if (this.activeHistoryIndex + step <= len - 1) {
      const lastData = this.history[this.activeHistoryIndex]
      this.activeHistoryIndex += step
      this.mindMap.emit(
        'back_forward',
        this.activeHistoryIndex,
        this.history.length
      )
      const data = simpleDeepClone(this.history[this.activeHistoryIndex])
      this.emitDataUpdatesEvent(lastData, data)
      return data
    }
  }

  /**
   * 获取渲染树的深拷贝数据
   * @private
   * @returns 渲染树副本
   */
  public getCopyData(): any {
    if (!this.mindMap.renderer.renderTree) return null
    const res = copyRenderTree({}, this.mindMap.renderer.renderTree, true)
    res.smmVersion = pkg.version
    return res
  }

  /**
   * 移除节点数据中的uid
   * @param data 节点数据
   * @returns 处理后的数据
   */
  removeDataUid(data: any): any {
    data = simpleDeepClone(data)
    const walk = (root: any): void => {
      delete root.data.uid
      if (root.children && root.children.length > 0) {
        root.children.forEach((item: any) => {
          walk(item)
        })
      }
    }
    walk(data)
    return data
  }

  /**
   * 发送数据更新详情事件
   * @private
   * @param lastData 更新前的数据
   * @param data 更新后的数据
   */
  private emitDataUpdatesEvent(lastData: any, data: any): void {
    try {
      const eventName = 'data_change_detail'
      const count = this.mindMap.event.listenerCount(eventName)
      if (count > 0 && lastData && data) {
        const lastDataObj = simpleDeepClone(transformTreeDataToObject(lastData))
        const dataObj = simpleDeepClone(transformTreeDataToObject(data))
        const res: Array<{
          action: 'create' | 'update' | 'delete'
          data: any
          oldData?: any
        }> = []

        const walkReplace = (root: any, obj: any): any => {
          if (root.children && root.children.length > 0) {
            root.children.forEach((childUid: any, index: number) => {
              root.children[index] =
                typeof childUid === 'string'
                  ? obj[childUid]
                  : obj[childUid.data.uid]
              walkReplace(root.children[index], obj)
            })
          }
          return root
        }

        // 处理新增和修改的节点
        Object.keys(dataObj).forEach(uid => {
          if (!lastDataObj[uid]) {
            res.push({
              action: 'create',
              data: walkReplace(dataObj[uid], dataObj)
            })
          } else if (!isSameObject(lastDataObj[uid], dataObj[uid])) {
            res.push({
              action: 'update',
              oldData: walkReplace(lastDataObj[uid], lastDataObj),
              data: walkReplace(dataObj[uid], dataObj)
            })
          }
        })

        // 处理删除的节点
        Object.keys(lastDataObj).forEach(uid => {
          if (!dataObj[uid]) {
            res.push({
              action: 'delete',
              data: walkReplace(lastDataObj[uid], lastDataObj)
            })
          }
        })

        this.mindMap.emit(eventName, res)
      }
    } catch (error) {
      this.mindMap.opt.errorHandler(
        ERROR_TYPES.DATA_CHANGE_DETAIL_EVENT_ERROR,
        error
      )
    }
  }
}

export default Command