import get from 'lodash/get'
import set from 'lodash/set'
import customForm from '@/common/customform.js'

/**
 * 工作流表单数据处理相关方法
 * (用于工作流表单页面,包含拉取、提交等函数,注意不可用于自定义应用和代码生成器页面)
 * 
 * 用户新建表单的流程:
 *  1、拉取流程信息
 *      调用 fetchProcessInfo、 getCurrentNode
 *  2、根据流程信息里的表单相关定义来拉取表单 scheme
 *      调用 fetchSchemeData
 *     (如果是使用移动端本地表单,则无需拉取表单,且略去后续所有步骤)
 *  3、加载选单数据 (例如 checkbox、select 这些表单项有选单数据)
 *  4、赋予默认值 (例如 currentInfo 类表单项,自动把当前用户的信息填入)
 *  5、生成表单
 *      调用 getCustomForm 即可完成步骤 3~4
 * 
 * 用户打开已经填写的表单/草稿的流程:
 *  1、拉取流程信息
 *       调用 fetchProcessInfo、 getCurrentNode
 *  2、拉取表单 scheme;如果是使用移动端本地表单则无需此步骤
 *       调用 fetchSchemeData
 *  3、加载选单数据
 *       调用 fetchFormData
 *  4、拉取表单中已填入的值
 *  5、如果打开的是草稿,可能要把部分未填的项赋默认值
 *  6、生成表单
 *       调用 getCustomForm 即可完成步骤 4~5
 * 
 * (以上只是简单介绍;实际使用中,如果打开子流程,需要拉取父/子两个流程信息)
 */
export default {
  mixins: [customForm],

  methods: {
    /**
     * 从流程信息中生成 scheme、formValue
     * 参数: { schemeData (必填), processId, currentNode, formData (新建时为 null), code, useDefault }
     * 返回: { scheme, formValue, rel }
     * 
     * 参数:
     *  schemeData: 使用 fetchSchemeData 方法拉取到的原始 scheme 数据,未经过格式化处理
     *  processId: 表单 GUID;如果是新建表单,可以用 this.GUID('-') 生成一个
     *  currentNode: 使用 getCurrentNode 方法拉取到的当前节点信息,用于权限控制
     *  formData: 填入表单的表单值,新建表单时此项为 null 即可
     *  code: 表单编号 code,会被赋值到返回的 formValue.code;重新发起流程的场合赋 null
     *  useDefault: 如果 formData 中某一项为空,是否对这一项填入默认值;通常在编辑草稿时启用
     * 
     * 该方法返回的 scheme 项可能带有以下属性:
     *   __valuePath__: 表单值在 formValue 中的路径,使用 lodash 的 get、set 方法即可读写
     *   __sourceData__: 表单值的选单数据
     *   __defaultItem__: 类型为 girdtable 的表单项带有此属性,表示添加一行表格时候表格项的默认值
     *   __schemeIndex__: (暂时用不到)表单项位于 schemeData 根级内的第几个子项中
     *   __dataIndex__: (暂时用不到)表单项位于 F_Scheme.data 中的第几个子项中
     */
    async getCustomForm(prop) {
      const { schemeData, formData, currentNode, code, processId, useDefault } = prop

      // 处理字段之间的级联、绑定关系
      // 需要绑定 change 事件的:
      //   datetime: 修改后重新计算 datetimerange
      //   organize: 修改后重设级联到该组件的其他组件的值,user 一级无需处理
      // 需要绑定某值的:
      //   organize: 级联到某个组件,company 一级无需处理
      const schemeRef = {}
      const refList = []

      // 最终返回值:scheme、rel、formValue
      const scheme = []
      const rel = {}
      const formValue = { processId, formreq: [] }
      if (code) {
        formValue.code = code
      }

      // 遍历 schemeData 中所有的 scheme
      const schemeList = Array.isArray(schemeData) ? schemeData : Object.values(schemeData)
      for (let schemeIndex = 0; schemeIndex < schemeList.length; ++schemeIndex) {
        const schemeItem = schemeList[schemeIndex]
        schemeItem.F_Scheme = JSON.parse(schemeItem.F_Scheme)
        // 已有表单值的时候,舍弃掉不存在表单值中的 scheme
        if (formData && !formData[schemeItem.F_SchemeInfoId]) {
          continue
        }
		
        // 设置 formreq 的内容,非新建模式下需要设置 keyValue
        const { formId, field } = get(currentNode, `wfForms.${schemeIndex}`, {})
        const formreqObj = { schemeInfoId: formId, processIdName: field, formData: {} }
        if (formData) {
          if (Object.values(get(formData, `${schemeItem.F_SchemeInfoId}`, {})).some(t => t && t.length > 0)) {
            formreqObj.keyValue = processId
          }
        }
        formValue.formreq[schemeIndex] = formreqObj

        for (let dataIndex = 0; dataIndex < schemeItem.F_Scheme.data.length; ++dataIndex) {
          const { componts } = schemeItem.F_Scheme.data[dataIndex]
          for (const t of componts) {
            // 之后的 t 即表示每个 scheme 项
            t.__valuePath__ = `formreq.${schemeIndex}.formData.${t.id}`
            // 以下两个属性暂时用不到
            t.__schemeIndex__ = schemeIndex
            t.__dataIndex__ = dataIndex

            if (t.type === 'girdtable' && t.table) {
              // 数据项是表格的情况
              // 先设置源数据,不然无法获取默认值
              for (const fieldItem of t.fieldsData) {
                fieldItem.__sourceData__ = await this.getSourceData(fieldItem)
              }
              t.__defaultItem__ = await this.getDefaultData(t, prop)
              if (formData) {
                // 有表单值的情况,从表单值中获取数据
                const val = []
                for (const valueItem of get(formData, `${schemeItem.F_SchemeInfoId}.${t.table}`, [])) {
                  const tableItemValue = {}
                  for (const fieldItem of t.fieldsData.filter(t => t.field)) {
                    const formDataValue = get(valueItem, fieldItem.field.toLowerCase())
                    tableItemValue[fieldItem.field] = await this.convertToFormValue(fieldItem, formDataValue)
                  }

                  val.push(tableItemValue)
                }

                // useDefault 表示在从 formData 取不到值的时候使用默认值
                if ((!val || val.length <= 0) && useDefault) {
                  set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)])
                } else {
                  set(formValue, t.__valuePath__, val)
                }

              } else {
                // 无表单值的情况,默认值
                set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)])
              }

            } else if (t.field) {
              // 数据项不是表格的情况
              // 先设置源数据,不然无法获取默认值
              t.__sourceData__ = await this.getSourceData(t)
              if (formData) {
                // 有表单值的情况,从表单值中获取数据
                const path = `${schemeItem.F_SchemeInfoId}.${t.table}.${dataIndex}.${t.field.toLowerCase()}`
                const formDataValue = get(formData, path)

                // useDefault 表示在从 formData 取不到值的时候使用默认值
                if (!formDataValue && useDefault) {
                  set(formValue, t.__valuePath__, await this.getDefaultData(t, prop))
                } else {
                  set(formValue, t.__valuePath__, await this.convertToFormValue(t, formDataValue))
                }

              } else {
                // 无表单值的情况,默认值
                set(formValue, t.__valuePath__, await this.getDefaultData(t, prop))
              }
            }

            // 权限控制
            const authObj = get(currentNode, `wfForms.${schemeIndex}.authorize.${t.id}`, {})
            t.edit = authObj.isEdit
            if (Number(t.isHide) !== 1 && authObj.isLook !== 0) {
              // 加入 scheme
              scheme.push(t)

              // organize、datetime 可能作为其他 organize 或 datetimerange 的依赖项,引用它们
              if (['organize', 'datetime'].includes(t.type)) {
                schemeRef[t.id] = t
              }

              // datetimerange、带有 relation 级联字段的 organize,依赖其他项
              if ((t.type === 'datetimerange' && t.startTime && t.endTime) || (t.type === 'organize' && t.relation)) {
                refList.push(t)
              }
            }
          }
        }
      }

      // 依次处理表单关联
      refList.forEach(t => {
        if (t.type === 'organize') {
          // 处理组件结构级联
          // 给当前组件赋上级级联的值路径 __relationPath__
          const parent = schemeRef[t.relation]
          t.__relationPath__ = parent.__valuePath__
          // 给上级级联的组件注册自动重置当前组件的 change 事件
          const relItem = { type: 'organize', id: t.id, path: t.__valuePath__ }
          rel[parent.id] = rel[parent.id] ? rel[parent.id].concat(relItem) : [relItem]

        } else if (t.type === 'datetimerange') {
          // 处理日期区间
          const start = schemeRef[t.startTime]
          const end = schemeRef[t.endTime]

          const relItem = {
            type: 'datetimerange',
            path: t.__valuePath__,
            id: t.id,
            startPath: start.__valuePath__,
            endPath: end.__valuePath__,
          }

          rel[start.id] = rel[start.id] ? rel[start.id].concat(relItem) : [relItem]
          rel[end.id] = rel[end.id] ? rel[end.id].concat(relItem) : [relItem]

        }
      })

      return { scheme, formValue, rel }
    },

    /**
     * 获取最终需要 POST 的数据
     * 参数:formValue, scheme
     * 返回:用于提交的数据
     * 
     * 遍历 formValue,将其中的表单值依次使用 convertToPostData 这个方法转化为提交值
     */
    async getPostData(originFormValue, scheme) {
      const formValue = this.COPY(originFormValue)

      // 依次按照 scheme 项目遍历
      for (const item of scheme) {
        if (item.field) {
          // 不是表格的情况
          const path = item.__valuePath__
          const val = get(formValue, path)
          const result = await this.convertToPostData(item, val, originFormValue, scheme)
          set(formValue, path, result)

        } else if (item.table && item.fieldsData) {
          // 是表格的情况
          const tableValue = get(formValue, item.__valuePath__, [])
          for (let valueIndex = 0; valueIndex < tableValue.length; ++valueIndex) {
            for (const schemeItem of item.fieldsData) {
              const path = `${item.__valuePath__}.${valueIndex}.${schemeItem.field}`
              const val = get(formValue, path)
              const result = await this.convertToPostData(schemeItem, val, originFormValue, scheme)
              set(formValue, path, result)
            }
          }
        }
      }

      formValue.formreq.forEach(t => { t.formData = JSON.stringify(t.formData) })
      formValue.formreq = JSON.stringify(formValue.formreq)

      return formValue
    },

    /**
     * 获取流程信息
     * 参数: { code, processId, taskId }
     * 
     */
    async fetchProcessInfo({ code, processId, taskId }) {
      const url = processId ? 'learun/adms/newwf/processinfo' : 'learun/adms/newwf/scheme'
      const reqObj = { processId }
      if (taskId) {
        reqObj.taskId = taskId
      }
      const data = processId ? reqObj : code
      const result = await this.HTTP_GET(url, data)

      if (!result) { return {} }

      if (result.info) {
        result.info.Scheme = JSON.parse(result.info.Scheme)
      } else if (result.F_Content) {
        result.F_Content = JSON.parse(result.F_Content)
      }

      return result
    },

    /**
     * 从 processInfo 流程信息中,提取出 currentNode
     * 参数: processInfo
     * 
     */
    getCurrentNode(processInfo) {
      if (processInfo.info) {
        return processInfo.info.Scheme.nodes.find(t => t.id === processInfo.info.CurrentNodeId)
      } else if (processInfo.F_Content) {
        return processInfo.F_Content.nodes.find(t => t.type === 'startround')
      }

      return {}
    },

    /**
     * 拉取表单的 schemeData
     * 参数: currentNode
     * 
     * 从当前节点 currentNode 中提取出表单 id,然后自 API 地址 /form/scheme 中拉取表单数据并返回
     */
    async fetchSchemeData(currentNode, currentTask, type) {
      const { wfForms } = currentNode

      const data = wfForms.filter(t => t.formId).map(t => ({ id: t.formId, ver: '' }))
      const schemeData = await this.HTTP_GET('learun/adms/form/scheme', data)

      return schemeData || {}
    },

    /**
     * 拉取表单的 formData
     * 参数: currentNode, keyValue
     * 
     * 提取当前节点信息、表单主键信息,从 API 地址 /form/data 中拉取表单数据
     */
    async fetchFormData({ wfForms }, keyValue) {
      const reqData = wfForms
        .filter(t => t.formId)
        .map(t => ({
          schemeInfoId: t.formId,
          processIdName: t.field,
          keyValue
        }))

      const formData = await this.HTTP_GET('learun/adms/form/data', reqData)

      return formData || {}
    }
  }
}