import type { INestApplication } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule, type OpenAPIObject } from '@nestjs/swagger';
import { cleanupOpenApiDoc } from 'nestjs-zod';
import { wrapOpenApiResponses } from './wrap-openapi';

/**
 * OpenAPI 单一来源 —— 运行时(main.ts 挂 /api/docs)与文档站(dump CLI 生成静态 spec)
 * 共用同一份构建逻辑,保证「文档 = 代码」零漂移。
 */

/** 全局路由前缀 */
export const GLOBAL_PREFIX = 'pac/v1';

/** 不挂前缀的路径(健康检查 / API 文档 / Bull Board UI) */
export const GLOBAL_PREFIX_EXCLUDE = [
  'health',
  'api/docs',
  'api/openapi.json',
  // Bull Board UI(@bull-board/nestjs 自己挂 Express handler,不走 Nest 路由前缀)
  'admin/queues',
  'admin/queues/*path',
];

/** tag → 中文标签 + 说明(文档站侧栏分组 / Scalar UI 用)。未列出的 tag 原样保留。 */
const TAG_DESCRIPTIONS: Record<string, string> = {
  auth: '登录换票、会话、数据权限(orgScope)。',
  facts: '患者事实写入(事件账本)与查询。',
  patient: '患者主档、检索、时间线。',
  persona: '患者画像与特征。',
  plan: '随访计划:生成、分配、回收、统计。',
  assistant: '实时坐席助手 / 会话辅助。',
  sync: '数据摄入与增量同步。',
  push: '触达结果回推 host(HMAC 验签)。',
  admin: '后台管理:映射缺失、AI 调用、host 自助。',
  mcp: 'MCP(Model Context Protocol)工具端点。',
  internal: '内部任务(日报预览 / 触发等)。',
  health: '健康检查。',
};

/**
 * nestjs 只在 operation 上挂 tag,不生成顶层 `tags` 列表 —— 而文档站按 tag 分组、
 * Scalar UI 也靠它排序。这里从所有 operation 收集 tag,补成顶层 `tags`(带中文说明)。
 */
function ensureTopLevelTags(doc: OpenAPIObject): OpenAPIObject {
  const seen = new Set<string>();
  for (const pathItem of Object.values(doc.paths ?? {})) {
    if (!pathItem) continue;
    for (const method of ['get', 'post', 'put', 'patch', 'delete'] as const) {
      const op = pathItem[method];
      for (const t of op?.tags ?? []) seen.add(t);
    }
  }
  doc.tags = [...seen]
    .sort()
    .map((name) => ({ name, ...(TAG_DESCRIPTIONS[name] ? { description: TAG_DESCRIPTIONS[name] } : {}) }));
  return doc;
}

/** 构建 PAC 的 OpenAPI 文档:zod 清洗 + 统一响应包络 + 顶层 tags。 */
export function buildPacOpenApiDocument(app: INestApplication): OpenAPIObject {
  const config = new DocumentBuilder()
    .setTitle('PAC API')
    .setDescription(
      'Patient Analysis Center — three-pool framework (facts in, plans out, agents inside).',
    )
    .setVersion('0.1.0')
    .addBearerAuth(
      { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', name: 'Authorization', in: 'header' },
      'accessToken',
    )
    .build();

  return ensureTopLevelTags(
    wrapOpenApiResponses(cleanupOpenApiDoc(SwaggerModule.createDocument(app, config))),
  );
}
