import { Test } from '@nestjs/testing';
import { PERSONA_FEATURE_SPECS, PERSONA_TAG_FILTER_DIMS, PERSONA_FEATURE_META } from '@pac/types';
import {
  FeatureRegistry,
  FEATURE_EXTRACTOR_PROVIDERS,
} from '../src/modules/persona/features/feature.registry';

/**
 * 标签注册表防漂移。
 *
 * 由来:同一个「这个标签怎么算的」历史上有四份 —— PERSONA_FEATURE_SPECS.algorithm、
 * 前端 persona-feature-hover.tsx 的 ALGORITHMS、extractor 顶部 JSDoc、extractor 代码。
 * 前端那份靠人肉同步,已经漂了:
 *   · 权益身份 hover 只列 2 类,extractor 实际产 5 类,还承诺了数据里没有的「保司 + 日期」
 *   · 转介绍达人 hover 写「社交型 = 推外人 ≥3 人」,真实门槛是「推荐 ≥3 人且带来成交」
 * 客服照着 hover 跟患者讲,说明写错比不写更糟。
 *
 * 现在说明收进标签卡(spec.display),前端只渲染不编内容。本文件守住"注册表 ↔ 实现"的一致:
 * 光收拢不设闸,过两个月照样会漂。
 */

/** extractor 都是无依赖纯函数类 → 不用起 PersonaModule(那会拽进 Prisma / ClinicalGap) */
async function registryKeys(): Promise<string[]> {
  const mod = await Test.createTestingModule({
    providers: [FeatureRegistry, ...FEATURE_EXTRACTOR_PROVIDERS],
  }).compile();
  return mod.get(FeatureRegistry).extractors.map((e) => e.key as string);
}

describe('PERSONA_FEATURE_SPECS ↔ FeatureRegistry', () => {
  test('⭐ 每个在跑的 extractor 都必须在注册表里登记(新特征上线不许漏登记)', async () => {
    const missing = (await registryKeys()).filter((k) => !PERSONA_FEATURE_SPECS[k]);
    expect(missing).toEqual([]);
  });

  test('⭐ 注册表里不留已摘除的标签(摘 extractor 时要一并清掉,否则覆盖率日报会永远报 0%)', async () => {
    const live = new Set(await registryKeys());
    const stale = Object.keys(PERSONA_FEATURE_SPECS).filter((k) => !live.has(k));
    expect(stale).toEqual([]);
  });
});

describe('spec.display —— 详情页说明', () => {
  test('⭐ 每个标签都要有客服版说明,且至少一条取值条目', () => {
    for (const [key, spec] of Object.entries(PERSONA_FEATURE_SPECS)) {
      expect(spec.display?.subtitle?.length ?? 0).toBeGreaterThan(0);
      expect(spec.display?.rules?.length ?? 0).toBeGreaterThan(0);
      for (const r of spec.display.rules) expect(r.body.length).toBeGreaterThan(0);
      expect(`${key}:${spec.display.subtitle}`).not.toContain('undefined');
    }
  });

  test('每个标签都有中文展示名(前端 hover 标题取它)', async () => {
    for (const key of await registryKeys()) expect(PERSONA_FEATURE_META[key]).toBeDefined();
  });
});

describe('PERSONA_TAG_FILTER_DIMS —— 圈人字典', () => {
  test('⭐ 可筛维度的 key 必须是在跑的标签(筛一个不产出的标签 = 永远 0 结果)', async () => {
    const live = new Set(await registryKeys());
    const orphan = PERSONA_TAG_FILTER_DIMS.filter((d) => !live.has(d.key)).map((d) => d.key);
    expect(orphan).toEqual([]);
  });

  test('圈人维度的取值不能为空', () => {
    for (const d of PERSONA_TAG_FILTER_DIMS) {
      expect(d.options.length).toBeGreaterThan(0);
      for (const o of d.options) expect(o.value.length).toBeGreaterThan(0);
    }
  });
});
