import * as _ from 'lodash';
import { FlowContext } from './context';
import { FlowResult } from './result';

/**
 * 节点配置
 */
export interface NodeConfig {
  // 输入参数
  inputParams: {
    /** 节点ID */
    nodeId: string;
    /** 参数名 */
    name: string;
    /** 类型(哪个节点) */
    nodeType: string;
    /** 字段 */
    field?: string;
    /** 值类型 */
    type: string;
    /** 是否必填 */
    required: boolean;
    /** 默认值 */
    default: any;
    /** 参数值 */
    value: any;
  }[];
  // 输出参数
  outputParams: {
    /** 节点ID */
    nodeId: string;
    /** 参数名 */
    name: string;
    /** 字段 */
    field?: string;
    /** 字段类型 */
    type: string;
    /** 类型(哪个节点) */
    nodeType: string;
  }[];
  // 配置
  options: Record<string, any>;
}

/**
 * 节点
 */
export abstract class FlowNode {
  /** 节点id */
  id: string;
  /** 流程id */
  flowId: number;
  /** 节点label */
  label: string;
  /** 节点类型 */
  type: string;
  /** 节点配置 */
  config: NodeConfig;
  /** 输入参数 */
  inputParams: any;
  /** 上下文 */
  context: FlowContext;
  /**
   * 调用
   * @param config
   */
  async invoke(context: FlowContext) {
    this.context = context;
    this.inputParams = this.getInputParams(context);
    return await this.run(context);
  }
  /**
   * 获得前缀
   * @returns
   */
  getPrefix() {
    return `${this.type}.${this.id}`;
  }

  /**
   * 获得参数前缀
   * @param param
   * @returns
   */
  getParamPrefix(param: { nodeId: string; nodeType: string }) {
    return `${param.nodeType}.${param.nodeId}`;
  }

  /**
   * 获取输入参数
   * @param context
   */
  private getInputParams(context: FlowContext) {
    if (context.isDebugOne() || this.type == 'start') {
      return context.getRequestParams();
    }
    const { inputParams } = this.config;
    // 如果是开始节点，参数从请求中获取 context.getRequestParams() 转换为 Map<string, any>
    let datas = context.getData('output') as Map<string, any>;
    let params = {};
    if (_.isEmpty(inputParams)) {
      return params;
    }
    for (const param of inputParams) {
      if (param.field) {
        params[param.field] = datas.get(
          `${this.getParamPrefix(param)}.${param.name}`
        );
      }
    }
    this.inputParams = params;
    return params;
  }

  /**
   * 执行
   * @param context
   */
  abstract run(context: FlowContext): Promise<FlowResult>;
}
