import { Provide, Scope, ScopeEnum } from '@midwayjs/core';

/** 全局键鼠互斥锁 — 系统只有一对键鼠 / 一块屏幕，任意时刻一个 task 占用前台 */
@Provide()
@Scope(ScopeEnum.Singleton)
export class DesktopMutex {
  /** 当前持锁方信息，null 表示空闲 */
  private busy: { taskId: string; appId: string } | null = null;

  /** 等待队列，FIFO */
  private waiters: Array<{
    resolve: () => void;
    reject: (err: Error) => void;
    taskId: string;
    appId: string;
  }> = [];

  /**
   * 获取锁，返回 release 函数。
   * 若当前有任务持锁则挂起等待，直到前序持锁方调用 release。
   * Abort 由调用方在 await 前后自行处理（throwIfAborted）。
   */
  async acquire(taskId: string, appId: string): Promise<() => void> {
    // 锁已被占用，进入等待队列
    if (this.busy) {
      await new Promise<void>((resolve, reject) => {
        this.waiters.push({ resolve, reject, taskId, appId });
      });
    }
    // 设置当前持锁方
    this.busy = { taskId, appId };

    let released = false;
    return () => {
      // release 幂等：重复调用无副作用
      if (released) return;
      released = true;
      this.busy = null;
      // 唤醒队列中的下一个等待者
      const next = this.waiters.shift();
      if (next) next.resolve();
    };
  }

  /** 检查当前锁是否被持有 */
  isBusy(): boolean {
    return this.busy !== null;
  }

  /** 返回当前持锁信息（测试 / 监控用），空闲时返回 null */
  current(): { taskId: string; appId: string } | null {
    return this.busy ? { ...this.busy } : null;
  }

  /**
   * 取消所有等待中的 waiter（外部 abort 时清理队列用）。
   * 不影响当前持有锁的 task，调用方在 release 时自然让位。
   */
  cancelWaiters(reason: string): void {
    const list = this.waiters.splice(0);
    for (const w of list) {
      w.reject(new Error(`mutex-canceled: ${reason}`));
    }
  }
}
