import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import {
  buildOrgTree,
  buildTree,
  expandOrgScope,
  type ExpandedScope,
  type OrgTree,
} from './org-scope';

/**
 * host org 树解析 + scope 展开 —— **从摄入数据派生,零硬编码**。
 *
 * 设计 B:JWT 只装通用 orgScope;**每个请求**在 tenant-scope 拦截器 / MCP 鉴权处调
 * expandScope() 按 host org 树展开成内部过滤维(sourceUnits + clinicIds)。
 *
 * 树不写死任何 id —— 从 `patient_transactions`(立柱 clinic_id + source_unit)派生
 * 「品牌 → 诊所」映射,对**任何 host、任何环境**自适应(指到别的 DW 自动跟着变)。
 * 按 (hostId, tenantId) 缓存(TTL);数据日更,够鲜。无数据 → 退化树(根=tenantId,无子)。
 */
@Injectable()
export class OrgTreeService {
  constructor(private readonly prisma: PrismaService) {}

  private readonly cache = new Map<string, { tree: OrgTree; at: number }>();
  private readonly ttlMs = 10 * 60 * 1000; // 10min
  private readonly refreshMinMs = 30 * 1000; // 强制重建防抖:30s 内不重复查(挡 bogus ref 刷库)

  /** 取 host org 树(缓存;miss/过期或 forceRefresh 时从摄入数据派生)。 */
  async getTree(hostId: string, tenantId: string, forceRefresh = false): Promise<OrgTree> {
    const key = `${hostId}:${tenantId}`;
    const hit = this.cache.get(key);
    const age = hit ? Date.now() - hit.at : Infinity;
    if (hit && !forceRefresh && age < this.ttlMs) return hit.tree;
    if (hit && forceRefresh && age < this.refreshMinMs) return hit.tree; // 防抖

    // 从摄入数据派生「品牌(source_unit)→ 诊所(clinic_id)」:
    //   clinic_id 立柱在 patient_transactions,source_unit 在 patients → join patient_id。
    // 显式 host+tenant 过滤(此处在 ALS 之前跑,纵深防御扩展不兜,自带条件)。
    const rows = await this.prisma.$queryRaw<{ source_unit: string; clinic_id: string }[]>`
      SELECT DISTINCT p.source_unit, pt.clinic_id
      FROM patient_transactions pt
      JOIN patients p ON p.id = pt.patient_id
      WHERE pt.host_id = ${hostId}::uuid AND pt.tenant_id = ${tenantId}
        AND p.source_unit <> '' AND pt.clinic_id <> ''
    `;
    const brandClinics: Record<string, string[]> = {};
    for (const r of rows) {
      (brandClinics[r.source_unit] ??= []).push(r.clinic_id);
    }
    const tree =
      Object.keys(brandClinics).length > 0
        ? buildOrgTree(tenantId, brandClinics)
        : buildTree({ id: tenantId }); // 无数据 → 退化树
    this.cache.set(key, { tree, at: Date.now() });
    return tree;
  }

  /** 把 orgScope(任意层级 org 节点)展开成内部过滤维 { sourceUnits, clinicIds }。 */
  async expandScope(
    hostId: string,
    tenantId: string,
    orgScope: string[],
  ): Promise<ExpandedScope> {
    let r = expandOrgScope(await this.getTree(hostId, tenantId), orgScope);
    // 有 ref 树里找不到 → 树缓存可能陈旧(新诊所/品牌刚摄入),强制重派生一次再展开,
    // 既避免误拒合法新诊所用户,也避免陈旧退化树把品牌制用户 fail-open(见 org-scope 的 DENY)。
    if (r.unresolved.length > 0) {
      r = expandOrgScope(await this.getTree(hostId, tenantId, true), orgScope);
    }
    return { sourceUnits: r.sourceUnits, clinicIds: r.clinicIds };
  }
}
