import { Injectable } from '@nestjs/common';
import { PersonaFeatureKey } from '@pac/types';
import type {
  FeatureExtractor,
  FeatureExtractorContext,
  PersonaFeatureDraft,
} from './feature.interface';
import { ageYearsAt, classifyGapToLabel } from './potential-treatment.feature';
import { visitFactsOf, visitSpanOf } from './visit-facts';
import { nextDayBoundary } from './time-boundary';

/**
 * urgency_level 急迫等级(C.2.1)— 规则层,snapshot · 单一标签(取最大)
 *
 * 标签:紧急 / 高 / 中 / 低。客户当前所有待处理事项中**最高**的急迫程度,决定外呼优先级。
 *
 * ⚠️ v1 只做【潜在治疗路径】(用户口径:已治疗复查路径召回未实现该场景,先不做)。
 *   潜在治疗路径(复用 ctx.potentialGaps = 潜在待转):
 *     末诊 > 90 天  → 紧急(有未满足需求且久未回诊,正在流失)
 *     末诊 30-90 天 → 高
 *     末诊 < 30 天 或 新发现 → 中(刚来过/刚发现,还在跟进中)
 *     美学/预防性建议 → 低(当前 8 标签不含美学/预防 → 暂不触发)
 *   无潜在治疗 → 不打标签(无待处理事项,此路径无急迫)。
 *
 * 末诊口径同 lifecycle_stage(单一源):max occurredAt of encounter / actual treatment / 挂号。
 * Step 2 取最大:多路径时 MAX;现仅 1 路径。已治疗复查路径 = follow-up(待召回实现复查场景)。
 */
@Injectable()
export class UrgencyLevelFeatureExtractor implements FeatureExtractor {
  readonly key = PersonaFeatureKey.URGENCY_LEVEL;
  private static readonly DAY = 86400_000;

  extract(ctx: FeatureExtractorContext): PersonaFeatureDraft | null {
    // 潜在待转 = 映射到 8 业务标签的 gap(与 potential_treatment 同一源,口径一致;
    //   K00/K09/年龄外的 gap 不算"潜在治疗路径"待处理项 → 不计急迫)。
    const age = ageYearsAt(ctx.patient.birthDate, ctx.now);
    const labelKeys = new Set<string>();
    const factIds = new Set<string>();
    for (const g of ctx.potentialGaps ?? []) {
      const lbl = classifyGapToLabel(g, age);
      if (lbl) {
        labelKeys.add(lbl.key);
        factIds.add(g.factId);
      }
    }
    if (labelKeys.size === 0) return null; // 无潜在待转(8 标签)→ 此路径无急迫
    const gaps = ctx.potentialGaps ?? [];

    // 末诊(末次就诊)— 口径见 visit-facts.ts,与 rfm / lifecycle_stage 同源
    const { last: lastVisit, lastFactId } = visitSpanOf(visitFactsOf(ctx));
    const last = lastVisit?.getTime() ?? null;
    if (lastFactId) factIds.add(lastFactId);
    const now = ctx.now.getTime();
    // 无就诊记录(罕见,有诊断必有诊)→ 回退用 gap 最早诊断天数
    const lastDays =
      last !== null
        ? Math.floor((now - last) / UrgencyLevelFeatureExtractor.DAY)
        : Math.max(...gaps.map((g) => g.daysSince));

    let code: string;
    let zh: string;
    if (lastDays > 90) {
      code = 'urgent';
      zh = '紧急';
    } else if (lastDays >= 30) {
      code = 'high';
      zh = '高';
    } else {
      code = 'mid';
      zh = '中';
    }

    const pendingLabels = labelKeys.size;
    return {
      key: this.key,
      description: `${zh} · 末诊${lastDays}天 · ${pendingLabels} 类潜在治疗`,
      score: null,
      data: { level: code, levelZh: zh, lastVisitDays: lastDays, pendingTypes: pendingLabels, path: 'potential_treatment' },
      evidence: { factIds: [...factIds] },
    };
  }

  /**
   * 档界只看「距末诊天数」:30(中→高)、90(高→紧急)。跨过 90 之后不再变 → null。
   * 注:无潜在待转时本特征不产出,但边界照报无妨 —— 重算一次发现还是不产出,写入侧判 unchanged。
   */
  nextBoundaryAt(ctx: FeatureExtractorContext): Date | null {
    const { last } = visitSpanOf(visitFactsOf(ctx));
    return nextDayBoundary(last, ctx.now, [30, 90]);
  }

}
