export class ServiceError extends Error {
  code?: number

  origin?: string

  constructor(message: string, origin?: string, code?: number) {
    super(message)
    this.code = code
    this.origin = origin
    this.stack = `${this.message}\n${this.origin}\n${this.code || ''}`
  }

  toString() {
    return this.message
  }
}

export class Service {
  throw(message: string, origin?: string, code?: number) {
    throw new ServiceError(message, origin, code)
  }
}

export function injectable<T extends { new(..._args: any[]): {} }> (Ctor: T) {
  let instance!: any
  return new Proxy(Ctor, {
    construct(t, args) {
      if (!instance) {
        instance = new Ctor(args)
        // console.log('instance ' + Ctor.name)
      }
      return instance
    }
  })
}
const runnerMap: { [key: string]: ((_res: any) => void)[] | undefined } = {}
/**
   * 互斥注解
   * 用于保证某个方法同一时间只有单次调用
   */
export function mutex(target: any, property: string) {
  const oriFn = target[property]
  const funcKey = `${target.constructor.name}-${property}`
  Object.defineProperty(target, property, {
    async value(...args: any[]) {
      const key = funcKey + JSON.stringify(args)
      if (runnerMap[key]) {
        return await new Promise((res) => {
          runnerMap[key]?.push((result: any) => res(result))
        })
      }
      runnerMap[key] = []

      setTimeout(() => {
        runnerMap[key] = undefined
      }, 4000)
      const res = await Reflect.apply(oriFn, this, args || [])
      runnerMap[key]?.forEach((fn) => fn(res))
      runnerMap[key] = undefined
      return res
    }
  })
  return target[property]
}