'use client';

import { diagnosisCodeNameZh, treatmentCategoryNameZh, lookupDxTreatment } from '@pac/types';
import { cn } from '@/lib/utils';
import type { AdaptedFact } from './adapt-data';
import type { PlanReason } from './mock-data';
import {
  runRecallOracle,
  reconcile,
  verdictMeta,
  type OracleVerdict,
  type ReconRow,
} from './recall-oracle';

/**
 * ToothTimeline — 每颗牙(+ 全口治疗线)的事实时间轴
 *
 * 设计:零推断、忠实展示。把临床 fact 按"治疗问题"分泳道:
 *   - 按牙的病 → 泳道键 = 牙位(多牙位 fact 拆进每颗牙)
 *   - 全口的病 → 泳道键 = 治疗线类别(牙周 / 正畸 / 全口·其他),按 category/诊断码分桶
 * 每条泳道内 = 时间倒序的事实流。不做 lifecycle / 合并 / 替代闭环(那些归召回 SQL)。
 */
const CLINICAL_TYPES = new Set([
  'diagnosis_record',
  'treatment_record',
  'recommendation_record',
  'image_record',
]);

const WHOLE_PERIO = '全口 · 牙周';
const WHOLE_ORTHO = '全口 · 正畸';
const WHOLE_OTHER = '全口 · 其他';

export function ToothTimeline({
  facts,
  reasons = [],
}: {
  facts: AdaptedFact[];
  /// 生产召回结果(plan.reasons)— 传入则跑独立 oracle 做对账验证
  reasons?: PlanReason[];
}) {
  const clinical = facts.filter((f) => CLINICAL_TYPES.has(f.type));
  if (clinical.length === 0) {
    return <div className="text-center py-12 text-sm text-slate-400">无牙位事实</div>;
  }

  // ── 独立 oracle:重算每颗牙/全口的召回判定,跟生产 reasons 对账 ──
  const verdicts = runRecallOracle(facts, new Date());
  const prodSignals = reasons.map((r) => r.signals ?? null);
  const recon = reconcile(verdicts, prodSignals);
  // 每条泳道的 oracle 判定(供泳道头展示徽标)
  const verdictsByLane = new Map<string, OracleVerdict[]>();
  for (const v of verdicts) {
    const arr = verdictsByLane.get(v.laneKey) ?? [];
    arr.push(v);
    verdictsByLane.set(v.laneKey, arr);
  }

  // 分桶
  const lanes = new Map<string, AdaptedFact[]>();
  const push = (k: string, f: AdaptedFact) => {
    const arr = lanes.get(k) ?? [];
    arr.push(f);
    lanes.set(k, arr);
  };
  for (const f of clinical) {
    // ⭐ 全口病(牙周 K05 / 正畸 K07 诊断、periodontic/orthodontic 治疗)无视 dx 自带牙位,
    //   直接进全口泳道 —— 跟召回 wholeMouth 口径一致。否则慢性牙龈炎(全口诊断列了 28 颗牙)
    //   会被拆进每颗牙,让 11/12/13 等只有全口病的牙位凭空出现单独泳道(显示假象)。
    if (isWholeMouthFact(f)) {
      push(wholeMouthLane(f), f);
      continue;
    }
    const teeth = factTeeth(f);
    if (teeth.length > 0) {
      for (const t of teeth) push(t, f);
    } else {
      push(wholeMouthLane(f), f);
    }
  }

  // 排序:牙位泳道(数值序)→ 牙周 → 正畸 → 全口其他
  const toothKeys = [...lanes.keys()]
    .filter((k) => ![WHOLE_PERIO, WHOLE_ORTHO, WHOLE_OTHER].includes(k))
    .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
  const wholeKeys = [WHOLE_PERIO, WHOLE_ORTHO, WHOLE_OTHER].filter((k) => lanes.has(k));
  const orderedKeys = [...toothKeys, ...wholeKeys];

  const tkey = (f: AdaptedFact) => {
    const t = f.occurredAt ?? f.plannedFor;
    return t ? new Date(t).getTime() : -Infinity;
  };
  // 临床顺序 rank:诊断 1 → 影像 2 → 建议 3 → 计划 4 → 治疗 5。
  // 整体时间倒序(新→旧),同一时刻也跟着倒:治疗 → 计划 → 建议 → 影像 → 诊断。
  const typeRank = (f: AdaptedFact) => {
    if (f.type === 'diagnosis_record') return 1;
    if (f.type === 'image_record') return 2;
    if (f.type === 'recommendation_record') return 3;
    if (f.type === 'treatment_record') return f.kind === 'planned' ? 4 : 5;
    return 6;
  };

  return (
    <div className="space-y-2.5">
      <ReconPanel recon={recon} />
      {orderedKeys.map((k) => {
        const rows = [...lanes.get(k)!].sort(
          (a, b) => tkey(b) - tkey(a) || typeRank(b) - typeRank(a),
        );
        const isWhole = k.startsWith('全口');
        const laneVerdicts = verdictsByLane.get(k) ?? [];
        return (
          <div key={k} className="rounded-lg border border-slate-200 overflow-hidden">
            <div
              className={cn(
                'flex items-center gap-2 px-2.5 py-1.5 text-[12px] font-semibold border-b border-slate-100',
                isWhole ? 'bg-amber-50 text-amber-800' : 'bg-slate-50 text-slate-700',
              )}
            >
              <span className="tabular-nums">{isWhole ? k : `牙位 ${k}`}</span>
              {laneVerdicts.map((v, i) => (
                <span
                  key={`${v.subKey}-${i}`}
                  title={`${v.subLabel} · ${diagnosisCodeNameZh(v.code) || v.code} — ${v.detail}`}
                  className={cn(
                    'inline-flex items-center gap-1 px-1.5 py-px rounded border text-[10px] font-medium',
                    verdictMeta(v.kind).tone,
                  )}
                >
                  {v.kind === 'recall' && <span className="w-1 h-1 rounded-full bg-emerald-500" />}
                  {verdictMeta(v.kind).zh}
                  <span className="font-normal opacity-70">{v.subLabel}</span>
                </span>
              ))}
              <span className="ml-auto text-[10px] font-normal text-slate-400">{rows.length} 项</span>
            </div>
            <div className="divide-y divide-slate-50">
              {rows.map((f) => (
                <ToothFactRow key={`${k}-${f.id}`} fact={f} />
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/// 对账面板:oracle(独立第二实现)vs 生产 plan_reasons。分歧 = bug 捕获点。
function ReconPanel({
  recon,
}: {
  recon: { rows: ReconRow[]; agree: number; oracleOnly: number; prodOnly: number };
}) {
  const { rows, agree, oracleOnly, prodOnly } = recon;
  const hasDiff = oracleOnly > 0 || prodOnly > 0;
  return (
    <div
      className={cn(
        'rounded-lg border overflow-hidden',
        hasDiff ? 'border-rose-200' : 'border-emerald-200',
      )}
    >
      <div
        className={cn(
          'flex items-center gap-2 px-2.5 py-1.5 text-[11.5px] font-semibold border-b',
          hasDiff
            ? 'bg-rose-50 text-rose-800 border-rose-100'
            : 'bg-emerald-50 text-emerald-800 border-emerald-100',
        )}
      >
        <span>召回对账</span>
        <span className="font-normal text-[10.5px] opacity-70">oracle 独立重算 vs 生产</span>
        <span className="ml-auto flex items-center gap-2 tabular-nums">
          <span className="text-emerald-700">✓ {agree}</span>
          <span className={cn(prodOnly > 0 ? 'text-rose-700' : 'text-slate-400')}>
            ⚠ 仅生产 {prodOnly}
          </span>
          <span className={cn(oracleOnly > 0 ? 'text-amber-700' : 'text-slate-400')}>
            ⚠ 仅oracle {oracleOnly}
          </span>
        </span>
      </div>
      {rows.length === 0 ? (
        <div className="px-2.5 py-2 text-[11px] text-slate-400">无召回信号(两边均无)</div>
      ) : (
        <div className="divide-y divide-slate-50">
          {rows.map((r) => (
            <div key={r.key} className="flex items-center gap-2 px-2.5 py-1.5 text-[11.5px]">
              <span className="w-[88px] flex-none tabular-nums text-slate-600">{r.toothLabel}</span>
              <span className="flex-none text-slate-800">{r.subLabel}</span>
              <span className="flex-1 min-w-0 truncate text-[10.5px] text-slate-400" title={r.oracle?.detail ?? ''}>
                {r.oracle?.detail ?? '生产召回,oracle 无对应信号(诊断可能已被取代)'}
              </span>
              <ReconBadge mark={r.mark} />
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function ReconBadge({ mark }: { mark: ReconRow['mark'] }) {
  const meta =
    mark === 'agree'
      ? { zh: '✓ 一致', tone: 'bg-emerald-50 text-emerald-700 border-emerald-200' }
      : mark === 'prod_only'
        ? { zh: '⚠ 仅生产', tone: 'bg-rose-50 text-rose-700 border-rose-200' }
        : { zh: '⚠ 仅oracle', tone: 'bg-amber-50 text-amber-700 border-amber-200' };
  return (
    <span
      className={cn(
        'flex-none px-1.5 py-px rounded border text-[10px] font-medium whitespace-nowrap',
        meta.tone,
      )}
    >
      {meta.zh}
    </span>
  );
}

function ToothFactRow({ fact }: { fact: AdaptedFact }) {
  const tIso = fact.occurredAt ?? fact.plannedFor;
  const planned = !fact.occurredAt && !!fact.plannedFor;
  const dateStr = tIso
    ? (planned ? '约 ' : '') + new Date(tIso).toLocaleDateString('zh-CN').replace(/\//g, '.')
    : '—';
  const { badge, badgeTone, text } = factLabel(fact);
  return (
    <div className="flex items-baseline gap-2 px-2.5 py-1.5 text-[12px]">
      <span className="w-[74px] flex-none tabular-nums text-[10.5px] text-slate-400">{dateStr}</span>
      <span
        className={cn(
          'flex-none px-1.5 py-px rounded text-[10px] font-medium',
          badgeTone,
        )}
      >
        {badge}
      </span>
      <span className="flex-1 min-w-0 text-slate-800 leading-snug" title={text}>
        {text}
      </span>
    </div>
  );
}

function factLabel(f: AdaptedFact): { badge: string; badgeTone: string; text: string } {
  const c = (f.content ?? {}) as Record<string, unknown>;
  switch (f.type) {
    case 'diagnosis_record': {
      const code = String(c.code ?? '');
      const name = String(c.name_zh ?? c.name ?? '');
      const std = code ? diagnosisCodeNameZh(code) : '';
      const text = std && name && std !== name ? `${std} · ${name}` : std || name || '诊断';
      return { badge: '诊断', badgeTone: 'bg-rose-50 text-rose-700', text };
    }
    case 'treatment_record': {
      const cat = String(c.category ?? '');
      const sub = String(c.subtype ?? '');
      const catZh = cat ? treatmentCategoryNameZh(cat) : '';
      const text = sub || catZh || '治疗';
      const isPlanned = f.kind === 'planned';
      return {
        badge: isPlanned ? '计划' : catZh || '治疗',
        badgeTone: isPlanned ? 'bg-slate-100 text-slate-500' : 'bg-teal-50 text-teal-700',
        text: catZh && sub ? `${text}` : text,
      };
    }
    case 'recommendation_record': {
      const name = String(c.name ?? '');
      const code = String(c.code ?? '');
      return {
        badge: '建议',
        badgeTone: 'bg-amber-50 text-amber-700',
        text: name || (code ? diagnosisCodeNameZh(code) : '') || '医生建议',
      };
    }
    case 'image_record': {
      const mod = String(c.modality ?? '');
      const modZh = MODALITY_ZH[mod] ?? mod ?? '影像';
      return { badge: '影像', badgeTone: 'bg-slate-100 text-slate-500', text: modZh };
    }
    default:
      return { badge: '事实', badgeTone: 'bg-slate-100 text-slate-500', text: f.title ?? f.type };
  }
}

const MODALITY_ZH: Record<string, string> = {
  pa: '根尖片',
  bw: '咬翼片',
  pano: '曲面断层',
  cbct: 'CBCT',
  intraoral_photo: '口内照片',
};

/// 牙位归一(剥面/方位后缀,保留牙位 base);跟 facts-timeline 同义
function toothBase(t: string): string {
  const m = /^(\d{1,2}[A-E]?)/i.exec(t.trim());
  return m ? m[1]!.toUpperCase() : t.trim().toUpperCase();
}

function factTeeth(f: AdaptedFact): string[] {
  const c = (f.content ?? {}) as Record<string, unknown>;
  const tp = c.tooth_positions;
  const raw = Array.isArray(tp) ? tp.join(';') : String(c.tooth_position ?? tp ?? '');
  return raw
    .split(/[;,]+/)
    .map((s) => s.trim())
    .filter(Boolean)
    .map(toothBase);
}

/// 全口事实分桶:牙周(periodontic/K05)/ 正畸(orthodontic/K07)/ 其他(预防/检查…)
function wholeMouthLane(f: AdaptedFact): string {
  const c = (f.content ?? {}) as Record<string, unknown>;
  const cat = String(c.category ?? '');
  const code = String(c.code ?? '');
  if (cat === 'periodontic' || code === 'K05') return WHOLE_PERIO;
  if (cat === 'orthodontic' || code === 'K07') return WHOLE_ORTHO;
  return WHOLE_OTHER;
}

/// 全口病:无视 fact 自带牙位,归全口泳道(跟召回 wholeMouth 口径一致)。
///   - 诊断/建议:code 的 wholeMouth 标志(K05 牙周 / K07 正畸 / SRP_/ORTHO_CONSULT_RECOMMENDED)
///   - 治疗:category = periodontic / orthodontic(洁牙/刮治/正畸 全口性)
/// K06(牙龈/牙槽嵴)非 wholeMouth → 仍按牙;K02 龋等结构病按牙。
function isWholeMouthFact(f: AdaptedFact): boolean {
  const c = (f.content ?? {}) as Record<string, unknown>;
  if (f.type === 'treatment_record') {
    const cat = String(c.category ?? '');
    return cat === 'periodontic' || cat === 'orthodontic';
  }
  if (f.type === 'diagnosis_record' || f.type === 'recommendation_record') {
    const code = String(c.code ?? '');
    return !!lookupDxTreatment(code)?.wholeMouth;
  }
  return false;
}
