import ServiceException from '../expression/exception/ServiceException'
import LogicConsole from '../expression/instances/LogicConsole'
import ExpressionRunner from '../expression/ExpressionRunner'
import { getLogicConfigByNameAsync } from '@vue2-client/services/api/common'
import * as Plugins from './plugins/index'
import { post as postRequest, get as getRequest } from '@vue2-client/services/api/restTools'
import Expression from '../expression/core/Expression'

export default class LogicRunner {
  static logicConsoleInstance = new LogicConsole()

  // 存储正在运行的 Logic 任务，key 为 logicName，value 为 { promise, abortController, status }
  // status: 'running' - 运行中, 'completed' - 已完成(用于ignore模式判断)
  static runningTasks = new Map()

  // 当前正在执行的请求 ID（用于区分同一 logic 的多次调用）
  static currentRequestId = null

  // 存储请求 ID 到 abortController 的映射（用于 cancel 模式）
  static requestIdToAbortController = new Map()

  // 获取当前正在运行的任务的 abortSignal（供 indexedDB.getByWeb 获取 Logic 配置时使用）
  // 注意：Logic 中的 HTTP 业务请求不走此方法，而是通过 executeLogic 注入的专属 http 插件获取 signal
  static getCurrentAbortSignal () {
    if (LogicRunner.currentRequestId) {
      const controller = LogicRunner.requestIdToAbortController.get(LogicRunner.currentRequestId)
      if (controller) {
        return controller.signal
      }
    }
    // 降级：从 runningTasks 获取
    const logicName = LogicRunner.currentExecutingLogicName
    if (!logicName) return null
    const task = LogicRunner.runningTasks.get(logicName)
    if (task && task.status === 'running' && task.abortController) {
      return task.abortController.signal
    }
    return null
  }

  // 清理请求 ID 映射
  static clearRequestId (requestId) {
    if (requestId) {
      LogicRunner.requestIdToAbortController.delete(requestId)
    }
  }

  // 任务完成后保持"已完成"状态的时间(毫秒)，之后自动清理
  static COMPLETED_STATUS_TTL = 3000

  /**
   * 是否存在指定名称的Logic资源
   *
   * @param logicName Logic名称
   * @return 是否存在
   */
  static async has (logicName) {
    const result = await LogicRunner.getLogic(logicName, false)
    return result != null
  }

  /**
   * 执行Logic
   *
   * @param logicName      Logic名称
   * @param param          参数
   * @param mode           运行模式：undefined-普通模式，'ignore'-忽略重复，'cancel'-取消前一个
   * @return 执行结果
   */
  static async run (logicName, param, mode) {
    const taskKey = logicName
    // 检查已存在的任务
    const existingTask = LogicRunner.runningTasks.get(taskKey)

    // 模式1: ignore - 如果已存在运行中的任务，直接忽略当前调用
    if (mode === 'ignore') {
      if (existingTask && existingTask.status === 'running') {
        return null
      }
    }

    // 模式2: cancel - 中止前一次运行，执行当前调用
    if (mode === 'cancel') {
      if (existingTask && existingTask.status === 'running' && existingTask.abortController) {
        existingTask.abortController.abort()
      }
    }

    // 创建唯一请求 ID，用于区分同一 logic 的多次调用
    const requestId = `${logicName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`

    // 每次都创建新的 AbortController
    // cancel 模式下：旧的 abortController 已被 abort，用于取消前一次请求
    // 新的 abortController 用于当前请求
    const abortController = new AbortController()

    // 存储请求 ID 到 abortController 的映射
    LogicRunner.requestIdToAbortController.set(requestId, abortController)

    // 关键：先设置当前请求 ID 和 abortController，在调用 getLogic 之前就设置
    // 这样 getLogic 发起的 HTTP 请求也能使用正确的 abortSignal
    LogicRunner.currentExecutingLogicName = logicName
    LogicRunner.currentRequestId = requestId

    // 存储任务信息，设置状态为运行中
    LogicRunner.runningTasks.set(taskKey, { promise: null, abortController, status: 'running', requestId })

    const taskPromise = LogicRunner.executeLogic(logicName, param, abortController, requestId)

    // 更新 promise 引用
    const currentTask = LogicRunner.runningTasks.get(taskKey)
    if (currentTask) {
      currentTask.promise = taskPromise
    }

    try {
      const result = await taskPromise
      return result
    } finally {
      // 清理请求 ID 映射
      LogicRunner.clearRequestId(requestId)

      // 任务完成后更新状态为已完成，而不是直接删除
      const currentTask = LogicRunner.runningTasks.get(taskKey)
      if (currentTask && currentTask.promise === taskPromise) {
        currentTask.status = 'completed'

        // 延迟删除任务，让 ignore 模式可以检测到"刚完成"的状态
        setTimeout(() => {
          const task = LogicRunner.runningTasks.get(taskKey)
          if (task && task.status === 'completed') {
            LogicRunner.runningTasks.delete(taskKey)
          }
        }, LogicRunner.COMPLETED_STATUS_TTL)
      }
    }
  }

  /**
   * 实际执行 Logic 的内部方法
   */
  static async executeLogic (logicName, param, abortController, requestId) {
    LogicRunner.currentExecutingLogicName = logicName
    LogicRunner.currentRequestId = requestId

    const result = await LogicRunner.getLogic(logicName, false)
    if (!result || !result.source) {
      throw new ServiceException('Logic资源' + logicName + '未找到', 400)
    }
    const source = result.source.replace(/[\r\n]+/g, '')
    const paramStr = Expression.toJSONString(param)
    const logicLog = LogicConsole.createLogger('logic.' + logicName)
    logicLog.info(`执行Logic[${logicName}]，params: ${paramStr}`)

    const plugins = {}
    plugins.data = param
    plugins.log = logicLog
    plugins.ENV = result.$globalProp
    plugins.logic = this
    plugins.abortSignal = abortController.signal

    // 合并全局插件，再用绑定了当前 abortSignal 的专属 http 插件覆盖单例
    // 保证并发执行多个 Logic 时，各自的 http.post/get 使用自己的 signal，互不污染
    Object.assign(plugins, Plugins.default)
    const signal = abortController.signal
    plugins.http = {
      post (url, body, config) {
        return postRequest(url, body || {}, { ...config, signal, dedupe: false })
          .then(res => res && res.data !== undefined ? res.data : res)
      },
      get (url, params, config) {
        return getRequest(url, params, { ...config, signal, dedupe: false })
          .then(res => res && res.data !== undefined ? res.data : res)
      }
    }

    return await LogicRunner.runExpression(source, plugins)
  }

  /**
   * 执行原生表达式
   *
   * @param source 表达式内容
   * @param params 参数
   * @return 执行结果
   */
  static async runExpression (source, params) {
    return await ExpressionRunner.run(source, params)
  }

  static async getLogic (logicName, isDev) {
    return await getLogicConfigByNameAsync(logicName, undefined, isDev)
  }
}
