export const nodeMap: { [key: string]: string } = {
  startEvent: 'start-node',
  endEvent: 'end-node',
  userTask: 'user-task-node',
  task: 'normal-task-node',
  exclusiveGateway: 'exclusive-gateway-node',
  parallelGateway: 'parallel-gateway-node',
};

export function getAllByLocalName(root: Element | Document, localName: string) {
  const out: Element[] = [];
  const all = root.getElementsByTagName('*');
  // console.log('all elements:', all);
  for (let i = 0; i < all.length; i++) {
    const el = all[i] as Element;
    if ((el.localName || el.nodeName).toLowerCase() === localName.toLowerCase()) {
      out.push(el);
    }
  }
  return out;
}

// helper to parse a single executionListener element into our shape
function parseExecutionListener(el: Element) {
  const ev = el.getAttribute('event') || '';
  const id = el.getAttribute('id') || '';
  let lType = '';
  let value: string | undefined;
  let scriptFormat: string | undefined;
  let scriptType: string | undefined;
  let scriptContent: string | undefined;
  let resource: string | undefined;

  if (el.hasAttribute('class')) {
    lType = 'class';
    value = el.getAttribute('class') || undefined;
  }
  else if (el.hasAttribute('expression')) {
    lType = 'expression';
    value = el.getAttribute('expression') || undefined;
  }
  else if (el.hasAttribute('delegateExpression')) {
    lType = 'delegateExpression';
    value = el.getAttribute('delegateExpression') || undefined;
  }
  else {
    const scripts = getAllByLocalName(el, 'script');
    if (scripts.length) {
      const sc = scripts[0];
      scriptFormat = sc.getAttribute('scriptFormat') || undefined;
      resource = sc.getAttribute('resource') || undefined;
      const txt = sc.textContent ? sc.textContent.trim() : '';
      if (resource) {
        scriptType = 'external';
      }
      else if (txt) {
        scriptType = 'inline';
        scriptContent = txt;
      }
      lType = 'scriptFormat';
    }
  }

  const fields: { name: string, type: string, value: string }[] = [];
  const fieldEls = getAllByLocalName(el, 'field');
  for (const f of fieldEls) {
    const fname = f.getAttribute('name') || '';
    const child = Array.from(f.children).find(c => !!c);
    let ftype = '';
    let fvalue = '';
    if (child) {
      const tag = (child.localName || child.nodeName).toLowerCase();
      const txt = (child.textContent || '').trim();
      if (tag === 'string') {
        ftype = '字符串';
        fvalue = txt;
      }
      else if (tag === 'expression') {
        ftype = '表达式';
        fvalue = txt;
      }
      else {
        ftype = tag;
        fvalue = txt;
      }
    }
    fields.push({ name: fname, type: ftype, value: fvalue });
  }

  const elObj: any = {
    field: fields,
    event: ev,
    lType,
  };
  if (id) {
    elObj.id = id;
  }
  if (value !== undefined) {
    elObj.value = value;
  }
  if (scriptFormat) {
    elObj.scriptFormat = scriptFormat;
  }
  if (scriptType) {
    elObj.scriptType = scriptType;
  }
  if (scriptContent) {
    elObj.scriptContent = scriptContent;
  }
  if (resource) {
    elObj.resource = resource;
  }
  return elObj;
}

// 解析流程图-消息
function parseMessage(el: Element | Document): any[] {
  const messages: { messageId: string, messageName: string }[] = [];
  const msgEls = getAllByLocalName(el, 'message');
  for (const m of msgEls) {
    const id = m.getAttribute('id') || '';
    const name = m.getAttribute('name') || '';
    messages.push({ messageId: id, messageName: name });
  }

  return messages;
}

// 解析流程图-信号
function parseSignal(el: Element | Document): any[] {
  const signals: { signalId: string, signalName: string }[] = [];
  const sigEls = getAllByLocalName(el, 'signal');
  for (const s of sigEls) {
    const id = s.getAttribute('id') || '';
    const name = s.getAttribute('name') || '';
    signals.push({ signalId: id, signalName: name });
  }

  return signals;
}

// 解析扩展属性properties
function parseExtensionProperties(el: Element): any[] {
  const propertiesArr: { name: string, value: string }[] = [];
  const propsContainers = getAllByLocalName(el, 'properties');
  for (const pc of propsContainers) {
    const props = getAllByLocalName(pc, 'property');
    for (const p of props) {
      const name = p.getAttribute('name') || '';
      const value = p.getAttribute('value') || '';
      propertiesArr.push({ name, value });
    }
  }
  return propertiesArr;
}

// 解析文档元素documentation
function parseDocumentation(el: Element): string {
  let documentation = '';
  const docs = getAllByLocalName(el, 'documentation');
  if (docs.length) {
    // flow的documentation 排在第一个 所以直接取第一个
    documentation = docs[0].textContent || '';
  }
  return documentation;
}

function getflowInfo(el: Element | Document) {
  const allProcesses = getAllByLocalName(el, 'process');
  // console.log('allProcesses', allProcesses);
  let processEl: Element | null = null;
  // 目前process只有一个 所以取第一个
  if (allProcesses.length) {
    processEl = allProcesses[0];
  }
  const processId = processEl?.getAttribute('id') || '';
  const processName = processEl?.getAttribute('name') || '';

  // 目前只有flow才有message和signal 所以直接在process下取
  const messages = parseMessage(el);
  const signals = parseSignal(el);

  const executionListeners: any[] = [];

  const extEls = getAllByLocalName(processEl as Element, 'extensionElements');
  let propertiesArr: { name: string, value: string }[] = [];
  let documentation = '';
  // 如果存在extensionElements，则解析后续操作
  if (extEls.length) {
    const flowEl = extEls[0];

    const els = getAllByLocalName(flowEl, 'executionListener');
    for (const el of els) {
      const elObj = parseExecutionListener(el);
      executionListeners.push(elObj);
    }

    propertiesArr = parseExtensionProperties(flowEl);

    documentation = parseDocumentation(flowEl);
  }

  const flowObj = {
    id: processId,
    type: 'flow',
    title: processName,
    messageList: messages,
    signalList: signals,
    executionListenerList: executionListeners,
    properties: propertiesArr,
    otherInfo: {
      documentation,
    },
  };

  return flowObj;
}

// 获取用户任务信息
function getUserTaskInfo(el: Element) {
  const userTaskInfo: any = {
    candidateStrategy: undefined,
    userIds: [],
    roleIds: [],
    postIds: [],
    bmzd: '',
    deptIds: [],
    levelId: '',
    groupIds: [],
    yhzd: '',
    flowVar: '',
    skipExpression: '',
    rangeId: '1',
    deptLevelId: '1',
    deptGrounpId: '',
  };

  const userTaskProps: any = {};
  const candidateStrategyEls = getAllByLocalName(el, 'candidateStrategy');
  if (candidateStrategyEls.length) {
    userTaskInfo.candidateStrategy = Number(candidateStrategyEls[0].textContent) || 0;
    userTaskProps.candidateStrategy = userTaskInfo.candidateStrategy;
  }
  const candidateParamEls = getAllByLocalName(el, 'candidateParam');
  if (candidateParamEls.length) {
    const candidateParam = candidateParamEls[0].textContent || '';
    userTaskProps.candidateParam = candidateParam;
    // 进行数据转化
    switch (userTaskInfo.candidateStrategy) {
      case 10:
        userTaskInfo.roleIds = candidateParam.split(',');
        break;
      case 20:
      case 21:
        userTaskInfo.deptIds = candidateParam.split(',');
        break;
      case 22:
        userTaskInfo.postIds = candidateParam.split(',');
        break;
      case 23: {
        const arr = candidateParam.split('|');
        userTaskInfo.deptIds = arr[0].split(',');
        userTaskInfo.levelId = Number.parseInt(arr[1]);
        break;
      }
      case 30:
        userTaskInfo.userIds = candidateParam.split(',');
        break;
      case 37:
      case 38:
        userTaskInfo.levelId = Number.parseInt(candidateParam);
        break;
      case 40:
        userTaskInfo.groupIds = candidateParam.split(',');
        break;
      case 50:
        userTaskInfo.yhzd = candidateParam;
        break;
      case 51:{
        const temp = candidateParam.split('|');
        userTaskInfo.bmzd = temp[0];
        userTaskInfo.levelId = Number.parseInt(temp[1]);
        break;
      }
      case 60:
        userTaskInfo.flowVar = candidateParam;
        break;
      case 70:{
        const arr = candidateParam.split('|');
        userTaskInfo.rangeId = arr[0];
        if (userTaskInfo.rangeId === '1') {
          userTaskInfo.deptLevelId = Number.parseInt(arr[1]);
        }
        else {
          userTaskInfo.deptGrounpId = arr[1];
        }
        userTaskInfo.roleId = arr[2];
        break;
      }
      default:
        break;
    }
  }
  userTaskInfo.skipExpression = el.getAttribute('flowable:skipExpression') || '';

  return { userTask: userTaskInfo, props: userTaskProps };
}

function getApproveInfo(el: Element) {
  const resultInfo: any = {};
  const approveMethodEls = getAllByLocalName(el, 'approveMethod');
  if (approveMethodEls.length) {
    resultInfo.approveMethod = Number.parseInt(approveMethodEls[0].textContent || '');
    if (resultInfo.approveMethod === 2) {
      const completionConditionEls = getAllByLocalName(el, 'completionCondition');
      if (completionConditionEls.length) {
        const str = completionConditionEls[0].textContent || '';
        const numStr = str.split('=')[1].replaceAll('}', '').trim();
        const approvalModePrecent = Number.parseFloat(numStr) * 100;
        resultInfo.approvalModePrecent = approvalModePrecent;
      }
    }
  }
  return resultInfo;
}

function getCustomConfiguration(el: Element) {
  const customConfig: any = {};
  const approveTypeEls = getAllByLocalName(el, 'approveType');
  if (approveTypeEls.length) {
    customConfig.approveType = approveTypeEls[0].textContent || '';
  }
  const assignEmptyHandlerTypeEls = getAllByLocalName(el, 'assignEmptyHandlerType');
  if (assignEmptyHandlerTypeEls.length) {
    customConfig.assignEmptyHandlerType = assignEmptyHandlerTypeEls[0].textContent || '';
  }
  const assignStartUserHandlerTypeEls = getAllByLocalName(el, 'assignStartUserHandlerType');
  if (assignStartUserHandlerTypeEls.length) {
    customConfig.assignStartUserHandlerType = assignStartUserHandlerTypeEls[0].textContent || '';
  }
  const assignEmptyUserIdsEls = getAllByLocalName(el, 'assignEmptyUserIds');
  if (assignEmptyUserIdsEls.length) {
    const str = assignEmptyUserIdsEls[0].textContent || '';
    customConfig.assignEmptyUserIds = str.split(',');
  }
  const rejectHandlerTypeEls = getAllByLocalName(el, 'rejectHandlerType');
  if (rejectHandlerTypeEls.length) {
    customConfig.rejectHandlerType = rejectHandlerTypeEls[0].textContent || '';
  }

  const rejectReturnTaskIdEls = getAllByLocalName(el, 'rejectReturnTaskId');
  if (rejectReturnTaskIdEls.length) {
    customConfig.rejectReturnTaskId = rejectReturnTaskIdEls[0].textContent || '';
  }

  const buttonsSetting: any[] = [];
  const buttonsSettingEls = getAllByLocalName(el, 'buttonsSetting');
  const defaultNames = ['通过', '拒绝', '转办', '委派', '加签', '退回'];
  if (buttonsSettingEls.length) {
    for (const btnEl of buttonsSettingEls) {
      const id = btnEl.getAttribute('id') || '';
      const name = defaultNames[Number.parseInt(id) - 1];
      const displayName = btnEl.getAttribute('displayName') || '';
      const enable = btnEl.getAttribute('enable') || '';
      buttonsSetting.push({
        id,
        name,
        displayName,
        enable: enable === 'true',
      });
    }
  }
  customConfig.buttonsSetting = buttonsSetting;

  const fieldsPermissions: any[] = [];
  const fieldsPermissionsEls = getAllByLocalName(el, 'fieldsPermissions');
  if (fieldsPermissionsEls.length) {
    for (const fpEl of fieldsPermissionsEls) {
      const key = fpEl.getAttribute('field') || '';
      const name = fpEl.getAttribute('title') || '';
      const permission = fpEl.getAttribute('permission') || '';
      fieldsPermissions.push({
        key,
        name,
        permission,
      });
    }
  }
  customConfig.fieldsPermissions = fieldsPermissions;

  const signEnableEls = getAllByLocalName(el, 'signEnable');
  if (signEnableEls.length) {
    customConfig.signEnable = signEnableEls[0].textContent === 'true';
  }

  const reasonRequireEls = getAllByLocalName(el, 'reasonRequire');
  if (reasonRequireEls.length) {
    customConfig.reasonRequire = reasonRequireEls[0].textContent === 'true';
  }

  return customConfig;
}

function getNodesInfo(processEl: Element | Document, defaultEdgeIds: string[]) {
  const nodeTagNames = ['startEvent', 'endEvent', 'userTask', 'task', 'exclusiveGateway', 'parallelGateway'];
  const nodes: any[] = [];
  for (const tag of nodeTagNames) {
    const els = getAllByLocalName(processEl, tag);
    for (const el of els) {
      const id = el.getAttribute('id') || '';
      const title = el.getAttribute('name') || '';
      let formKey = '';
      if (tag === 'startEvent' || tag === 'userTask') {
        formKey = el.getAttribute('flowable:formKey') || '';
      }
      const elExecutionListeners: any[] = [];
      const executionListenerEls = getAllByLocalName(el, 'executionListener');
      for (const item of executionListenerEls) {
        const elObj = parseExecutionListener(item);
        elExecutionListeners.push(elObj);
      }

      const elProperties: { name: string, value: string }[] = parseExtensionProperties(el);

      const elDocumentation = parseDocumentation(el);

      let userTask: any = null;
      let approveInfo: any = null;
      const elTaskListeners: any[] = [];
      let customConfiguration: any = null;

      if (tag === 'userTask') {
        // 用户任务、多人审批方式、任务监听器、自定义配置
        userTask = getUserTaskInfo(el);
        approveInfo = getApproveInfo(el);

        const taskListenerEls = getAllByLocalName(el, 'taskListener');
        for (const item of taskListenerEls) {
          const elObj = parseExecutionListener(item);
          elTaskListeners.push(elObj);
        }
        customConfiguration = getCustomConfiguration(el);
      }

      // 如果存在default， 说明有一条边是默认的
      const defaultEdgeId = el.getAttribute('default') || '';
      if (defaultEdgeId) {
        defaultEdgeIds.push(defaultEdgeId);
      }

      nodes.push({
        id,
        type: nodeMap[tag],
        title,
        formKey,
        properties: elProperties,
        executionListener: elExecutionListeners,
        otherInfo: { documentation: elDocumentation },
        userTask: userTask ? userTask.userTask : undefined,
        userTaskProps: userTask ? userTask.props : undefined,
        approveMethod: approveInfo ? approveInfo.approveMethod : undefined,
        approvalModePrecent: approveInfo ? approveInfo.approvalModePrecent : undefined,
        taskListener: elTaskListeners,
        customConfiguration: customConfiguration || undefined,
      });
    }
  }
  return nodes;
}

function getEdgesInfo(processEl: Element | Document, defaultEdgeIds: string[]) {
  const edges: any[] = [];
  const els = getAllByLocalName(processEl, 'sequenceFlow');
  for (const el of els) {
    const id = el.getAttribute('id') || '';
    const title = el.getAttribute('name') || '';
    const edgeTransfer: any = { type: 'normal' };
    if (defaultEdgeIds.includes(id)) {
      edgeTransfer.type = 'half';
    }
    const conditionEls = getAllByLocalName(el, 'conditionExpression');
    if (conditionEls.length) {
      edgeTransfer.type = 'empty';
      const language = conditionEls[0].getAttribute('language') || '';
      if (language) {
        edgeTransfer.lType = 'scriptFormat';
        edgeTransfer.scriptFormat = language;
        if (conditionEls[0].textContent) {
          edgeTransfer.scriptType = 'inline';
          edgeTransfer.scriptContent = conditionEls[0].textContent;
        }
        else {
          edgeTransfer.scriptType = 'external';
          edgeTransfer.resource = conditionEls[0].getAttribute('flowable:resource') || '';
        }
      }
      else {
        edgeTransfer.lType = 'expression';
        edgeTransfer.expression = conditionEls[0].textContent;
      }
    }
    const elExecutionListeners: any[] = [];
    const executionListenerEls = getAllByLocalName(el, 'executionListener');
    for (const item of executionListenerEls) {
      const elObj = parseExecutionListener(item);
      elExecutionListeners.push(elObj);
    }

    const elProperties: { name: string, value: string }[] = parseExtensionProperties(el);

    const elDocumentation = parseDocumentation(el);

    edges.push({
      id,
      type: 'custom-bezier-edge',
      title,
      edgeTransfer: Object.keys(edgeTransfer).length ? edgeTransfer : undefined,
      properties: elProperties,
      executionListenerList: elExecutionListeners,
      otherInfo: { documentation: elDocumentation },
    });
  }
  return edges;
}

/**
 * 将 BPMN xml 解析为 flowDataList 样式的数据也是
 * @param xmlString BPMN xml 字符串
 */
export function getPropsData(xmlString: string): any[] {
  const parser = new DOMParser();
  const doc = parser.parseFromString(xmlString, 'text/xml');
  const flowObj = getflowInfo(doc);
  const defaultEdgeIds: string[] = [];
  const nodesList = getNodesInfo(doc, defaultEdgeIds);
  const edgesList = getEdgesInfo(doc, defaultEdgeIds);

  const flowDataList: any[] = [];
  flowDataList.push(flowObj);
  flowDataList.push(...nodesList);
  flowDataList.push(...edgesList);

  console.warn('flowDataList:', flowDataList);
  return flowDataList;
}
