import { ForbiddenException } from '@nestjs/common';
import type { TenantScopeContext } from './tenant-scope.decorator';

/**
 * 🔴 **诊所 id 的唯一入口** —— 不传就用登录人的第一个诊所,传了就**必须在他的范围里**。
 *
 * ── 由来(2026-08-10 测试机实测的越权)──────────────────────────
 * 北京朝阳公园的主管(`clinicIds = [66701845…]`)带着别家诊所的 id
 * (`7d49539c…` 杭州大厦)请求 `/plans/assignments/workload`,拿到了
 * **200 + 那家 26 位客服的姓名与负载**。`/agents`、`/plans/matrix` 同样敞着。
 *
 * ⚠️ 这三个都是**读**接口。写路径(创建批次)早就拦了
 *   (`plan-assignment.service` 里那句 `!scope.clinicIds.includes(dto.clinicId)`),
 *   所以分不走别家的人 —— 但名册、负载、整个池子矩阵能随便看。
 *   ⛔ 别因为"写拦住了"就觉得读无所谓:名册是**员工姓名**,矩阵是**患者量分布**。
 *
 * ⚠️ 触发它的不是攻击,是**前端第一帧**:`visibleClinics` 在 `/auth/session` 回来之前
 *   会回落到"字典里的全部诊所",于是工作台第一帧就带着别家 id 发了一次请求。
 *   前端那边也修了,但 ⛔ **前端修好不等于这里可以不拦** ——
 *   查询串是用户可改的,少了服务端这道闸,改个 URL 就越权。
 *
 * ⚠️ `scope.clinicIds` 为空 = **集团级范围**(看全部诊所),此时无从校验,原样放行。
 *   ⛔ 别把空数组当成"没有权限"去拒绝 —— 那会把集团角色全锁死。
 *
 * ⚠️ 抛 `ForbiddenException`(→ 10107),⛔ 不能"当成这个诊所没人"返回空:
 *   0 是一个合法答案,静默返回空会让调用方(尤其是助手)拿着 0 去解释
 *   "为什么这批人是空的",越解释越像真的(违 T14「口径对数」)。
 */
export function resolveClinicId(scope: TenantScopeContext, clinicId?: string): string {
  const given = clinicId?.trim();
  if (!given) {
    const fallback = scope.clinicIds[0];
    if (!fallback) {
      throw new ForbiddenException(
        '没有指定诊所,当前登录人也没有绑定诊所 —— 请先确认数据范围,⛔ 不要自己编一个诊所 id。',
      );
    }
    return fallback;
  }
  if (scope.clinicIds.length && !scope.clinicIds.includes(given)) {
    throw new ForbiddenException(
      `诊所 id「${given}」不在当前登录人的数据范围内(他的诊所是:${scope.clinicIds.join(' / ')})。`,
    );
  }
  return given;
}
