import ElementRegistry from 'diagram-js/lib/core/ElementRegistry';
import { typeTask, typeTrigger } from '../config/variableName';
import { changeTypeByTaskShape } from '../config';
export class BpmnLintError {
  public element: any;
  public message: string;
  constructor(element: any | any[], message: string) {
    this.element = element;
    this.message = message;
  }
}

// 自定义bpmnLint规则
abstract class BpmnLintBase {
  protected registry: ElementRegistry;
  protected root: any;
  protected constructor(registry: ElementRegistry, root: any) {
    this.registry = registry;
    this.root = root;
  }
  public abstract validate(): boolean;
  protected error: BpmnLintError | undefined;
  public getError(): BpmnLintError | undefined {
    return this.error;
  }
}
/**
 * xml元素校验
 *  1.流程必须要有一个【开始，结束】节点 且只能有一个。
 *  2.至少有一个【审批】节点，
 *  3.分流网关和合流官网必须成对关系。
 *  4.除了开始节点（只有出线）及结束节点（只有进线） 其它节点必须要含有出线和入线。
 *  5.分流节点出线必须有多条出线，只有一条进线； 合流节点必须有多条进线，只能有一条出线。
 *
 */
let jnpfProps: any;
class FormValidator extends BpmnLintBase {
  public constructor(registry: ElementRegistry, root: any, props: any) {
    super(registry, root);
    jnpfProps = props;
  }
  validate(): boolean {
    let errorList: any = [];
    let allElements = this.root.children;
    if (allElements && allElements.length > 0) {
      let newElementData = allElements.reduce((classified: any, element: any) => {
        // 获取元素类型 (网关元素存在分流和合流 所以需要取 wnGatewayType)
        let elementType = (element.type === 'bpmn:InclusiveGateway' ? element.wnGatewayType : element.type)?.replace('bpmn:', '')?.toLowerCase();
        // 如果分类对象中不存在当前类型的数组，则创建一个新数组
        if (!classified[elementType]) classified[elementType] = [];
        // 将元素添加到相应类型的数组中
        classified[elementType].push(element);
        return classified;
      }, {});
      if (newElementData) {
        if (!(newElementData.hasOwnProperty('startevent') && newElementData.hasOwnProperty('endevent') && newElementData.hasOwnProperty('usertask'))) {
          this.error = new BpmnLintError(null, '确保画布上至少包含【开始-审批-结束】节点');
          return false;
        }
        // 开始节点
        if (newElementData['startevent']) {
          if (newElementData['startevent'].length > 1) {
            // 开始 结束节点只有拥有一个
            this.error = new BpmnLintError(null, '画布上只能存在一个开始节点');
            return false;
          }
          // 开始节点只能有一个出线
          if (newElementData['startevent'][0]) {
            let outLine = newElementData['startevent'][0].outgoing;
            let inLine = newElementData['startevent'][0].incoming; // 进线
            if (inLine && inLine.length > 0) {
              errorList.push({
                element: newElementData['startevent'][0],
                error: '开始节点不存在进线，请删除多余线条！',
              });
            }
            if (!outLine) {
              errorList.push({
                element: newElementData['startevent'][0],
                error: '开始元素不能单独使用，请于其它元素进行连线使用！',
              });
            }
          }
        }
        // 结束节点：
        if (newElementData['endevent']) {
          if (newElementData['endevent'].length > 1) {
            this.error = new BpmnLintError(null, '画布上只能存在一个结束节点，请删除多余节点元素！');
            return false;
          }
          // 结束节点只能有一个进线
          if (newElementData['endevent'][0]) {
            let outLine = newElementData['endevent'][0].outgoing;
            let inLine = newElementData['endevent'][0].incoming; // 进线
            if (outLine && outLine.length > 0) {
              errorList.push({
                element: newElementData['endevent'][0],
                error: '结束节点不存在出线，请删除多余线条！',
              });
            }
            if (!inLine) {
              errorList.push({
                element: newElementData['endevent'][0],
                error: '结束元素不能单独使用，请于其它元素进行连线使用！',
              });
            }
          }
        }
        // 网关
        if (newElementData.hasOwnProperty('confluence') || newElementData.hasOwnProperty('divide')) {
          if (!(newElementData['confluence'] && newElementData['confluence'].length && newElementData['divide'] && newElementData['divide'].length)) {
            this.error = new BpmnLintError(null, '请您检查分流与合流是否成对使用！');
            return false;
          }
        }
        // 任务节点
        if (newElementData['usertask']) {
          // 遍历进线元素 必须有一条进线及出线元素
          let userTaskList = newElementData['usertask'];
          userTaskList.map((userTaskShape: any) => {
            let outLine = userTaskShape.outgoing;
            let inLine = userTaskShape.incoming; // 进线
            // 任务节点必须要有一个进线和出线
            if (!(outLine?.length && inLine?.length)) {
              if (!changeTypeByTaskShape[userTaskShape?.wnType] || (jnpfProps.type === 2 && changeTypeByTaskShape[userTaskShape?.wnType])) {
                errorList.push({
                  element: userTaskShape,
                  error: '元素不能单独使用，请确保连接至少一个执行节点',
                });
              } else if (changeTypeByTaskShape[userTaskShape?.wnType] && !inLine?.length) {
                errorList.push({
                  element: userTaskShape,
                  error: '执行元素不能单独使用，请确保连接到触发节点',
                });
              }
            }
            if (userTaskShape?.wnType === typeTask) {
              // 如果任务节点只有触发节点 则报错 需要连接其它节点或者结束节点。
              if (outLine.every(s => s.target.wnType === typeTrigger))
                errorList.push({
                  element: userTaskShape,
                  error: '审批元素不能只包含触发节点, 请确保至少有一条出线与其他元素相连接',
                });
            }
          });
        }
        if (errorList && errorList.length > 0) {
          this.error = new BpmnLintError(errorList, errorList[0].error);
          return false;
        }
      }
    } else {
      this.error = new BpmnLintError(null, '画布上不存在元素，至少包含【开始-审批-结束】节点');
      return false;
    }
    return true;
  }
}

export function validate(registry: ElementRegistry, root: any, props: any): BpmnLintError | undefined {
  /***/
  const validators = [new FormValidator(registry, root, props)];

  for (let validator of validators) {
    if (!validator.validate()) {
      return validator.getError();
    }
  }
}
