import { BaseService } from '@cool-midway/core';
import { ILogger, Inject } from '@midwayjs/core';
import { Provide } from '@midwayjs/decorator';
import * as moment from 'moment';
import { FlowContext } from '../runner/context';
import { FlowExecutor } from '../runner/exec';
import { FlowResult } from '../runner/result';
import { FlowInfoService } from './info';
import { FlowLogService } from './log';

/**
 * 运行流程
 */
@Provide()
export class FlowRunService extends BaseService {
  // 节点执行器
  @Inject()
  flowExecutor: FlowExecutor;

  @Inject()
  flowInfoService: FlowInfoService;

  @Inject()
  flowLogService: FlowLogService;

  @Inject()
  logger: ILogger;

  /**
   * 调试
   * @param params 参数
   * @param label 标签
   * @param nodeId 节点ID
   * @param callback 回调
   */
  async debug(
    params: any,
    label: string,
    nodeId: string,
    stream: boolean,
    callback: (res) => void
  ) {
    const start = moment().valueOf();
    try {
      // 上下文
      const context = new FlowContext();
      // 设置请求参数
      context.setRequestParams(params);
      // 调试的时候非流式调用
      context.setStream(stream);
      // 获得所有节点
      const { nodes, info } = await this.flowInfoService.getNodes(label, true);
      // 设置流程图
      context.setFlowGraph(info.draft);
      if (nodeId) {
        // 调试单个节点
        const node = nodes.find(item => item.id == nodeId);
        if (node) {
          // 设置调试参数
          context.setDebugOne(true);
          await this.flowExecutor.one(node, context, callback);
        }
      } else {
        // 调用流程
        const result = await this.flowExecutor.serial(nodes, context, callback);

        // 保存日志
        this.saveLog(result.success, {
          label: label,
          inputParams: params,
          result: result?.result ?? null,
          error: result?.error ?? null,
          nodeInfo: result?.nodesResult ?? null,
        });
        delete result?.nodesResult;
        return result;
      }
    } catch (error) {
      this.logger.error('业务错误', error);

      // 保存日志
      this.saveLog(
        false,
        {
          label: label,
          inputParams: params,
        },
        error
      );

      const end = moment().valueOf();
      callback &&
        callback({
          result: {
            error: {
              message: error.message,
            },
          },
          duration: end - start,
          isEnd: true,
        });
    }
  }

  /**
   * 调用
   * @param params 参数
   * @param label 标签
   * @param stream 是否流式调用
   * @returns
   */
  async invoke(
    params: any,
    label: string,
    stream = false
  ): Promise<FlowResult> {
    try {
      // 上下文
      const context = new FlowContext();
      // 设置开始参数
      context.set('start', params, 'input');
      // 设置请求参数
      context.setRequestParams(params);
      // 设置流式调用
      context.setStream(stream);
      // 获得所有节点
      const { nodes, info } = await this.flowInfoService.getNodes(label, true);
      // 设置流程图
      context.setFlowGraph(info.draft);
      // 调用流程
      const result = await this.flowExecutor.serial(nodes, context);

      // 保存日志
      this.saveLog(result.success, {
        label: label,
        inputParams: params,
        result: result?.result ?? null,
        error: result?.error ?? null,
        nodeInfo: result?.nodesResult ?? null,
      });
      delete result.nodesResult;
      return result;
    } catch (error) {
      // 保存日志
      this.saveLog(
        false,
        {
          label: label,
          inputParams: params,
        },
        error
      );
    }
  }

  /**
   * 保存日志
   * @param success 是否成功 true | false
   * @param data 数据
   * @param error 错误信息
   */
  async saveLog(
    success: boolean,
    data: {
      label: string;
      inputParams: any;
      result?: any;
      error?: any;
      nodeInfo?: any;
    },
    error?
  ) {
    try {
      const result = success ? data.result : error || data.error;

      const params: any = {
        flowLabel: data.label,
        type: success ? 1 : 0,
        inputParams: data.inputParams,
        nodeInfo: data.nodeInfo,
        result,
      };

      // 保存日志
      this.flowLogService.save(params);
    } catch (error) {
      this.logger.error('保存日志错误', error);
    }
  }
}
