import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import type { ActionStep, DesktopTask } from './types.js';

/** desktop_op_config 表里读出的形状（子集 — 只列 SafetyGuard 关心的字段） */
export interface SafetyGuardConfig {
  allowedApps?: string[];
  extraDangerousKeys?: string[];
}

/**
 * 安全护栏 — 应用白名单 + 危险按键 / 动作硬黑名单
 * 在 runtime.runTask 各阶段被调用，任何拦截抛 Error
 */
@Provide()
@Scope(ScopeEnum.Singleton)
export class SafetyGuard {
  /** 允许操作的应用 appId 白名单 */
  private allowedApps = new Set<string>(['weixin']);

  /** 被禁止的热键（全部小写存储，比较时统一转小写） */
  private dangerousKeys = new Set<string>([
    'delete',
    'win+r',
    'alt+f4',
    'win+l',
    'ctrl+alt+delete',
    'ctrl+shift+esc',
  ]);

  /** 被禁止的动作类型（硬黑名单） */
  private dangerousActionTypes = new Set<string>(['shell-command', 'kill-process']);

  /**
   * 校验 appId 是否在白名单内
   * 不在白名单则抛出 Error
   */
  validateAppId(appId: string): void {
    // v4 临时诊断:出错时打印当前白名单内容
    if (!this.allowedApps.has(appId)) {
      // eslint-disable-next-line no-console
      console.warn('[SafetyGuard DEBUG] app-not-allowed: %s, current allowedApps=%j', appId, [...this.allowedApps]);
      throw new Error('app-not-allowed: ' + appId);
    }
  }

  /**
   * 校验 ActionStep 是否安全
   * runtime 在 execute 前调用，危险动作直接抛出 Error
   */
  validateAction(step: ActionStep): void {
    // 热键类型：检查是否在危险键集合中（大小写不敏感）
    if (step.type === 'hotkey') {
      const keyLower = step.key.trim().toLowerCase();
      if (this.dangerousKeys.has(keyLower)) {
        throw new Error('dangerous-key-blocked: ' + step.key);
      }
    }
    // 危险动作类型硬黑名单
    if (this.dangerousActionTypes.has(step.type as string)) {
      throw new Error('dangerous-action-blocked: ' + step.type);
    }
  }

  /**
   * 校验 task 整体形状（必填字段不能为空）
   */
  validateTaskShape(task: DesktopTask): void {
    if (!task.id) throw new Error('invalid-task: id missing');
    if (!task.appId) throw new Error('invalid-task: appId missing');
    if (!task.actionType) throw new Error('invalid-task: actionType missing');
  }

  /**
   * 加载 desktop_op_config 配置
   * 启动时 + admin 后台 reload 时调用
   * - allowedApps 为空数组时不覆盖默认白名单
   * - extraDangerousKeys 只追加，不替换
   */
  async loadConfig(cfg: SafetyGuardConfig | null | undefined): Promise<void> {
    // cfg 为 null / undefined 时直接返回，保留默认值
    if (!cfg) return;

    // 非空数组才覆盖白名单
    if (Array.isArray(cfg.allowedApps) && cfg.allowedApps.length > 0) {
      this.allowedApps = new Set(cfg.allowedApps);
    }

    // 追加额外危险键（统一转小写）
    if (Array.isArray(cfg.extraDangerousKeys)) {
      for (const k of cfg.extraDangerousKeys) {
        if (typeof k === 'string') {
          this.dangerousKeys.add(k.toLowerCase());
        }
      }
    }
  }
}
