import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import type { AccessTokenPayload } from '@pac/types';
import { ROLE_PERMISSIONS } from '@pac/types';
import type { TenantScopeContext } from '../../common/decorators/tenant-scope.decorator';
import { OrgTreeService } from '../auth/org-tree';

export interface McpAuthContext {
  scope: TenantScopeContext;
  permissions: string[];
  /**
   * 登录人姓名(可空)。
   * ⚠️ 加它**不违 T19**:T19 说的是"权限判定只认 permission,不下传 role" ——
   * 姓名是**显示串**不是判据,助手要能说"张主管,这批要分给…"而不是对着一个 uuid 说话。
   * ⛔ 但绝不要顺手把 role 也带下来:一旦模型看得见 role,它就会自己发明
   * "leader 应该也能 X" 这类规则,而那不是权限模型说了算的。
   */
  userName?: string;
}

/**
 * McpAuthService — MCP 端点的 Bearer 鉴权。
 *
 * 复用平台 JWT(AuthModule 配置的 jwt.secret)。token payload 带 hostId/tenantId/orgScope/permissions,
 * → 派生 TenantScopeContext,每个工具调用据此**强制租户隔离**(与 REST 同一套边界)。
 */
@Injectable()
export class McpAuthService {
  constructor(
    private readonly jwt: JwtService,
    private readonly orgTree: OrgTreeService,
  ) {}

  async verifyBearer(authHeader: string | undefined): Promise<McpAuthContext> {
    if (!authHeader?.startsWith('Bearer ')) {
      throw new UnauthorizedException('缺 Bearer token');
    }
    const token = authHeader.slice('Bearer '.length).trim();
    let payload: AccessTokenPayload;
    try {
      payload = await this.jwt.verifyAsync<AccessTokenPayload>(token);
    } catch {
      throw new UnauthorizedException('token 无效或已过期');
    }
    if (!payload?.hostId || !payload?.tenantId) {
      throw new UnauthorizedException('token payload 缺 host/tenant');
    }
    // 设计 B:JWT 装通用 orgScope,这里展开成内部过滤维(与 REST 拦截器同一套,树从摄入数据派生)。
    const { sourceUnits, clinicIds } = await this.orgTree.expandScope(
      payload.hostId,
      payload.tenantId,
      payload.orgScope ?? [],
    );
    return {
      scope: {
        hostId: payload.hostId,
        tenantId: payload.tenantId,
        clinicIds,
        sourceUnits,
        userId: payload.sub,
      },
      // ⭐ 按 role 现算,与 PermissionsGuard / GET /auth/session 同源(见 permissions.guard 长注释)。
      //   MCP 这条路尤其要现算:工具清单是按能力**条件注册**的,权限少一个 = 助手手里少几个工具,
      //   模型只会说"我没有这个能力",既不报错也看不出是 token 过期。
      permissions: ROLE_PERMISSIONS[payload.role] ?? payload.permissions ?? [],
      // 宿主换票时把姓名放进 dictionary.users[sub];没有就留空,助手会退回"你"
      ...(resolveUserName(payload) ? { userName: resolveUserName(payload)! } : {}),
    };
  }
}

/** 从 JWT 字典里取当前登录人姓名(宿主换票时带;取不到返回 undefined) */
function resolveUserName(payload: AccessTokenPayload): string | undefined {
  const n = payload.dictionary?.users?.[payload.sub];
  return typeof n === 'string' && n.trim() ? n : undefined;
}
