/**
 * PR15 CLI Executor service skeleton —— 把 CliToolDefinition 渲染成 docker run argv 调用。
 *
 * **Skeleton 而非真实化的原因**：
 * - Docker daemon 在后端节点可访问（sock 挂载或 Docker SDK 鉴权）—— 部署侧未配
 * - per-tenant 镜像（ffai/cli-runner:*）—— 需在 infra repo 写 Dockerfile + 构建发布
 * - quota 联动 + sidecar egress proxy —— PR0.6 Day-1 Ops Baseline 一并落
 *
 * 真实化前 invoke() 显式抛错；descriptor 仍可注册到 ToolRegistry 让 LLM 看到工具列表。
 *
 * 详见 docs/modules/agent/02-architecture.md PR15 段。
 */

import { Injectable, BadRequestException } from '@nestjs/common';
import type {
  CliInvokeArgs,
  CliInvokeResult,
  CliParamSchema,
  CliToolDefinition,
} from './cli-executor.types';
import { PR15_DEFAULT_CLI_TOOLS } from './cli-executor.types';

@Injectable()
export class CliExecutorService {
  private readonly tools = new Map<string, CliToolDefinition>();

  constructor() {
    for (const def of PR15_DEFAULT_CLI_TOOLS) {
      this.tools.set(def.name, def);
    }
  }

  /**
   * 校验 + 渲染 argv，**不实际跑容器**（等 Docker 接入）。
   * 暴露给 ToolRegistry 用：注册时拿 descriptor，invoke 时调本方法获 argv。
   */
  resolveArgv(args: CliInvokeArgs): readonly string[] {
    const def = this.tools.get(args.toolName);
    if (!def) throw new BadRequestException(`unknown_cli_tool:${args.toolName}`);

    this.validateParams(def, args.params);

    return def.template.map((seg) => {
      const m = seg.match(/^\{(\w+)\}$/);
      if (!m) return seg;
      const key = m[1];
      const val = args.params[key];
      // pattern / range 已在 validateParams 校；这里只做安全 stringify
      return String(val);
    });
  }

  /**
   * PR15 真实化阶段实现：
   * - 用 dockerode 启 container（image=def.sandbox.image, network=def.sandbox.network, ...）
   * - 渲染 workdir 模板（{org_id} / {repo_slug} → args.orgId / args.params.repo_slug 等）
   * - 限定 timeout / cpu / memory
   * - 收 stdout/stderr（max 1 MiB 截断），返回 CliInvokeResult
   * - 销毁 container（finally）
   */
  async invoke(_args: CliInvokeArgs): Promise<CliInvokeResult> {
    throw new BadRequestException(
      'CliExecutorService.invoke not implemented: Docker per-tenant sandbox infrastructure required (PR0.6 + PR15 真实化阶段)',
    );
  }

  /** 全量 CLI 工具元数据（给 ToolRegistry / admin UI 用） */
  listDefinitions(): readonly CliToolDefinition[] {
    return [...this.tools.values()];
  }

  // ── private ──

  private validateParams(
    def: CliToolDefinition,
    params: Readonly<Record<string, unknown>>,
  ): void {
    for (const [key, schema] of Object.entries(def.paramSchemas)) {
      const val = params[key];
      if (val === undefined || val === null) {
        if (schema.required) throw new BadRequestException(`missing_param:${key}`);
        continue;
      }
      this.validateOne(key, val, schema);
    }
    for (const k of Object.keys(params)) {
      if (!def.paramSchemas[k]) {
        throw new BadRequestException(`unknown_param:${k}`);
      }
    }
  }

  private validateOne(name: string, val: unknown, schema: CliParamSchema): void {
    switch (schema.type) {
      case 'integer': {
        if (typeof val !== 'number' || !Number.isInteger(val)) {
          throw new BadRequestException(`param_type_mismatch:${name} expect integer`);
        }
        if (schema.min !== undefined && val < schema.min) {
          throw new BadRequestException(`param_below_min:${name}<${schema.min}`);
        }
        if (schema.max !== undefined && val > schema.max) {
          throw new BadRequestException(`param_above_max:${name}>${schema.max}`);
        }
        break;
      }
      case 'string': {
        if (typeof val !== 'string') {
          throw new BadRequestException(`param_type_mismatch:${name} expect string`);
        }
        if (schema.pattern && !new RegExp(schema.pattern).test(val)) {
          throw new BadRequestException(`param_pattern_mismatch:${name}`);
        }
        if (schema.enum && !schema.enum.includes(val)) {
          throw new BadRequestException(`param_not_in_enum:${name}`);
        }
        break;
      }
      case 'boolean': {
        if (typeof val !== 'boolean') {
          throw new BadRequestException(`param_type_mismatch:${name} expect boolean`);
        }
        break;
      }
    }
  }
}
