'use client';

import { useState, useRef, useEffect } from 'react';
import { diagnosisCodeNameZh, treatmentCategoryNameZh } from '@pac/types';
import { cn, formatToothPosition } from '@/lib/utils';
import type { AdaptedFact } from './adapt-data';

/**
 * EmrSoapView — 病历快读(SOAP 结构化 + 多 EMR carousel 翻页)
 *
 * 数据源:**纯 PAC 事实层**(emr_record + 同 source_encounter 的 diagnosis_record /
 * treatment_record / image_record),**不依赖 AI**。
 *
 * SOAP 分段:
 *   S Subjective(主观) — illness_desc + pre_illness + past_history + general_condition(自由文本)
 *   O Objective(客观) — exam_findings JSON array(按牙位)+ 关联影像 metadata
 *   A Assessment(诊断) — 同 source_encounter 的 diagnosis_record 列表(K 码 + 牙位 + 名)
 *   P Plan(计划)     — disposal + doctor_advice + 同 source_encounter 的 treatment_record(planned)
 *
 * 多 EMR carousel(轻量自实现,不引 embla):
 *   - 左右箭头切换 + 圆点 indicator + N/M 进度
 *   - 键盘 ← → 切换
 *   - 当前卡淡入展示;默认最新一次接诊(idx=0)
 */
export function EmrSoapView({ facts }: { facts: AdaptedFact[] }) {
  const emrs = facts
    .filter((f) => f.type === 'emr_record')
    .sort((a, b) => (b.occurredAt ?? '').localeCompare(a.occurredAt ?? ''));

  const [idx, setIdx] = useState(0);
  const total = emrs.length;
  const containerRef = useRef<HTMLDivElement>(null);

  // 键盘 ← → 切换(当前 drawer 内焦点时生效)
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (total <= 1) return;
      if (e.key === 'ArrowLeft') setIdx((i) => (i - 1 + total) % total);
      else if (e.key === 'ArrowRight') setIdx((i) => (i + 1) % total);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [total]);

  if (total === 0) {
    return <div className="py-8 text-center text-[12px] text-slate-400">无病历 fact</div>;
  }

  const current = emrs[idx]!;
  const prev = () => setIdx((i) => (i - 1 + total) % total);
  const next = () => setIdx((i) => (i + 1) % total);

  // 圆点窗口化:病历再多也只显示最多 MAX_DOTS 个点(跟随当前居中),避免横向溢出。
  const MAX_DOTS = 9;
  const dotIdxs: number[] =
    total <= MAX_DOTS
      ? emrs.map((_, i) => i)
      : Array.from({ length: MAX_DOTS }, (_, k) => {
          const half = Math.floor(MAX_DOTS / 2);
          const start = Math.max(0, Math.min(idx - half, total - MAX_DOTS));
          return start + k;
        });

  return (
    <div ref={containerRef} className="space-y-3">
      {/* 顶部 carousel 导航条 — 圆点 + N/M + 箭头 */}
      {total > 1 && (
        <div className="flex items-center justify-between gap-3 px-1">
          <button
            onClick={prev}
            aria-label="上一次接诊"
            className="inline-flex items-center justify-center w-7 h-7 rounded-md border border-slate-200 bg-white hover:bg-slate-50 hover:border-slate-300 text-slate-600 transition-colors"
          >
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="w-3.5 h-3.5">
              <path d="M15 18l-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </button>

          {/* 圆点 indicator(窗口化,最多 MAX_DOTS 个,首尾小点暗示还有更多) */}
          <div className="flex items-center gap-1.5 flex-1 justify-center min-w-0 overflow-hidden">
            {dotIdxs.map((i) => {
              const moreEdge =
                (i === dotIdxs[0] && i > 0) ||
                (i === dotIdxs[dotIdxs.length - 1] && i < total - 1);
              return (
                <button
                  key={emrs[i]!.id}
                  onClick={() => setIdx(i)}
                  aria-label={`第 ${i + 1} 次接诊`}
                  className={cn(
                    'h-1.5 rounded-full transition-all flex-none',
                    i === idx
                      ? 'w-6 bg-indigo-500'
                      : moreEdge
                        ? 'w-1 bg-slate-200'
                        : 'w-1.5 bg-slate-300 hover:bg-slate-400',
                  )}
                />
              );
            })}
          </div>

          <div className="flex items-center gap-2 flex-none">
            <span className="text-[11px] text-slate-500 tabular-nums">
              第 <span className="font-semibold text-slate-800">{total - idx}</span> / {total} 次
            </span>
            <button
              onClick={next}
              aria-label="下一次接诊"
              className="inline-flex items-center justify-center w-7 h-7 rounded-md border border-slate-200 bg-white hover:bg-slate-50 hover:border-slate-300 text-slate-600 transition-colors"
            >
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="w-3.5 h-3.5">
                <path d="M9 18l6-6-6-6" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </button>
          </div>
        </div>
      )}

      {/* 当前卡 — key 用 emr.id 保证切换时 React 重建,触发淡入动画
          visitIdx = 真实就诊顺序(最早 = 第 1 次,最近 = 第 N 次)。emrs 已按时间倒序,所以是 total - idx。 */}
      <div key={current.id} className="animate-[fadeIn_0.18s_ease-out]">
        <EmrSection
          emr={current}
          facts={facts}
          isLatest={idx === 0}
          totalCount={total}
          visitIdx={total - idx}
        />
      </div>
    </div>
  );
}

function EmrSection({
  emr,
  facts,
  isLatest,
  totalCount,
  visitIdx,
}: {
  emr: AdaptedFact;
  facts: AdaptedFact[];
  isLatest: boolean;
  totalCount: number;
  /// 真实就诊顺序号(1 = 最早就诊,N = 最近一次)
  visitIdx: number;
}) {
  const c = (emr.content as Record<string, unknown>) ?? {};
  // ⚠️ source_encounter_external_id 实际是病历 id(emr_external_id),不是 registration_id
  //   parser 里 diagnosis/treatment_planned/image 的 sourceEncounterExternalId = emr.id(ad575...)
  //   所以匹配同次接诊用 emr_external_id,不能用 encounter_external_id(那个是挂号号)
  const emrId = String(c.emr_external_id ?? '');
  const doctor = (c.doctor_name as string | undefined) ?? '—';
  const date = emr.occurredAt?.slice(0, 10) ?? '—';

  // 关联同 encounter 的 diagnosis / treatment(planned) / image
  const sameEncounter = (f: AdaptedFact) =>
    String((f.content as Record<string, unknown> | null)?.source_encounter_external_id ?? '') === emrId;
  const diagnoses = facts.filter((f) => f.type === 'diagnosis_record' && sameEncounter(f));
  const plannedTx = facts.filter((f) => f.type === 'treatment_record' && f.kind === 'planned' && sameEncounter(f));
  // 本次实际做的治疗(actual,同次接诊)— 病历里体现"这次到底做了什么"
  const actualTx = facts.filter((f) => f.type === 'treatment_record' && f.kind === 'actual' && sameEncounter(f));
  // 医生建议(同次接诊)— 忠实展示医生原文建议(如"建议更换"),含长尾(code 可能为空)
  const recommendations = facts.filter((f) => f.type === 'recommendation_record' && sameEncounter(f));
  const images = facts.filter((f) => f.type === 'image_record' && sameEncounter(f));

  // S 主观 — 自由文本
  const sFields = [
    { k: '主诉', v: c.illness_desc as string },
    { k: '现病史', v: c.pre_illness as string },
    { k: '既往史', v: c.past_history as string },
    { k: '一般情况', v: c.general_condition as string },
  ].filter((x) => !!x.v);

  // O 客观 — examine 按牙位拆
  const examFindings = parseJsonArray(c.exam_findings as string);

  // P 计划 — disposal(按牙位)+ doctor_advice
  const disposals = parseJsonArray(c.disposal as string);
  const doctorAdvice = (c.doctor_advice as string | undefined) ?? '';

  return (
    <section className="rounded-lg border border-slate-200 bg-white overflow-hidden">
      {/* 头:日期 + 医生 + 第几次 */}
      <header className="px-3 py-2 bg-slate-50 border-b border-slate-200 flex items-center justify-between flex-wrap gap-2">
        <div className="flex items-center gap-2 min-w-0">
          <span className="text-[12.5px] font-semibold text-slate-900 tabular-nums">{date}</span>
          <span className="text-[11px] text-slate-500">·</span>
          <span className="text-[11.5px] text-slate-700">{doctor} 医生</span>
        </div>
        <span className="text-[10.5px] text-slate-400 nums">
          {isLatest && totalCount > 1 && <span className="mr-1 text-emerald-600">最近</span>}
          第 {visitIdx} 次接诊
        </span>
      </header>

      <div className="p-3 space-y-3">
        {/* S */}
        {sFields.length > 0 && (
          <SoapBlock letter="S" tone="sky" title="主观(Subjective)">
            <div className="space-y-1.5">
              {sFields.map((x) => (
                <div key={x.k} className="text-[12px] text-slate-700 leading-relaxed">
                  <span className="text-[11px] text-slate-500 mr-1">{x.k}:</span>
                  {x.v}
                </div>
              ))}
            </div>
          </SoapBlock>
        )}

        {/* O */}
        {(examFindings.length > 0 || images.length > 0) && (
          <SoapBlock letter="O" tone="indigo" title="客观(Objective)">
            {examFindings.length > 0 && (
              <ul className="space-y-1 mb-2">
                {examFindings.map((x, i) => (
                  <li key={i} className="text-[12px] text-slate-700 leading-relaxed">
                    {x.toothPosition && (
                      <span className="inline-block px-1.5 py-0 mr-1.5 bg-slate-100 text-slate-700 rounded text-[10.5px] tabular-nums align-middle">
                        {formatToothPosition(x.toothPosition, 999)}
                      </span>
                    )}
                    {x.message}
                  </li>
                ))}
              </ul>
            )}
            {images.length > 0 && (
              <div className="flex items-center gap-1.5 text-[11px] text-slate-500">
                <svg viewBox="0 0 24 24" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="2">
                  <rect x="3" y="3" width="18" height="18" rx="2" />
                  <circle cx="8.5" cy="8.5" r="1.5" />
                  <path d="m21 15-5-5L5 21" />
                </svg>
                影像 {images.length} 张:
                {images.map((img, i) => {
                  const modality = (img.content as Record<string, unknown> | null)?.modality as string | undefined;
                  return (
                    <span key={i} className="px-1.5 py-px bg-slate-100 rounded text-[10px] text-slate-700">
                      {modality || '影像'}
                    </span>
                  );
                })}
              </div>
            )}
          </SoapBlock>
        )}

        {/* A */}
        {diagnoses.length > 0 && (
          <SoapBlock letter="A" tone="rose" title="评估(Assessment)">
            <ul className="space-y-1">
              {diagnoses.map((dx) => {
                const dc = dx.content as Record<string, unknown>;
                const code = String(dc.code ?? '');
                // badge 直接显示字典翻译后的诊断名(K05 → 慢性牙周炎),沿用 key 样式(rose)
                // host 原始 name_zh / name 在 host 端可能跟字典名略不同,但客服关心标准名 — 走字典
                const badgeText = code ? diagnosisCodeNameZh(code) : String(dc.name_zh ?? dc.name ?? '—');
                const tooth = String(dc.tooth_position ?? '');
                // 医生原始诊断文字(name_zh)— 与标准名不同才显示(如"无功能牙"→标准名"牙列丢失/缺牙")
                const rawName = String(dc.name_zh ?? dc.name ?? '').trim();
                const showRaw = rawName && rawName !== badgeText;
                // 影像 AI 抽出的诊断(非医生手写)— 标记区分,客服知道这是影像识别的信号
                const fromImage = String(dc.code_source ?? '') === 'image_ai';
                // 跟"本次治疗"同结构:[标签] 原文(深色) · 牙位
                return (
                  <li key={dx.id} className="text-[12px] text-slate-700 leading-relaxed">
                    <span className="px-1.5 py-px mr-1.5 bg-rose-50 text-rose-700 rounded text-[10.5px] font-medium">
                      {badgeText}
                    </span>
                    {showRaw && <span className="font-medium">{rawName}</span>}
                    {tooth && <span className="ml-1.5 text-[11px] text-slate-500">· 牙位 {formatToothPosition(tooth, 999)}</span>}
                    {fromImage && (
                      <span className="ml-1.5 px-1 py-px bg-sky-50 text-sky-600 rounded text-[10px] font-medium align-middle">
                        影像
                      </span>
                    )}
                  </li>
                );
              })}
            </ul>
          </SoapBlock>
        )}

        {/* P */}
        {(disposals.length > 0 || actualTx.length > 0 || plannedTx.length > 0 || recommendations.length > 0 || doctorAdvice) && (
          <SoapBlock letter="P" tone="emerald" title="计划(Plan)">
            {disposals.length > 0 && (
              <div className="mb-2">
                <div className="text-[11px] text-slate-500 mb-1">处置:</div>
                <ul className="space-y-1">
                  {disposals.map((x, i) => (
                    <li key={i} className="text-[12px] text-slate-700 leading-relaxed whitespace-pre-line">
                      {x.toothPosition && (
                        <span className="inline-block px-1.5 py-0 mr-1.5 bg-slate-100 text-slate-700 rounded text-[10.5px] tabular-nums align-middle">
                          {formatToothPosition(x.toothPosition, 999)}
                        </span>
                      )}
                      {x.message}
                    </li>
                  ))}
                </ul>
              </div>
            )}
            {actualTx.length > 0 && (
              <div className="mb-2">
                <div className="text-[11px] text-slate-500 mb-1">本次治疗:</div>
                <ul className="space-y-1">
                  {actualTx.map((tx) => {
                    const tc = tx.content as Record<string, unknown>;
                    const subtype = String(tc.subtype ?? '');
                    const cat = String(tc.category ?? '');
                    const badgeText = cat ? treatmentCategoryNameZh(cat) : '治疗';
                    const tooth = String(tc.tooth_position ?? '');
                    return (
                      <li key={tx.id} className="text-[12px] text-slate-700 leading-relaxed">
                        <span className="px-1.5 py-px mr-1.5 bg-teal-50 text-teal-700 rounded text-[10.5px] font-medium">
                          {badgeText}
                        </span>
                        <span className="font-medium">{subtype}</span>
                        {tooth && <span className="ml-1.5 text-[11px] text-slate-500">· 牙位 {formatToothPosition(tooth, 999)}</span>}
                      </li>
                    );
                  })}
                </ul>
              </div>
            )}
            {plannedTx.length > 0 && (
              <div className="mb-2">
                <div className="text-[11px] text-slate-500 mb-1">治疗计划:</div>
                <ul className="space-y-1">
                  {plannedTx.map((tx) => {
                    const tc = tx.content as Record<string, unknown>;
                    const subtype = String(tc.subtype ?? '');
                    const cat = String(tc.category ?? '');
                    // badge 显示字典翻译(periodontic → 牙周),沿用 key 样式(emerald)
                    const badgeText = cat ? treatmentCategoryNameZh(cat) : '治疗';
                    const tooth = String(tc.tooth_position ?? '');
                    return (
                      <li key={tx.id} className="text-[12px] text-slate-700 leading-relaxed">
                        <span className="px-1.5 py-px mr-1.5 bg-emerald-50 text-emerald-700 rounded text-[10.5px] font-medium">
                          {badgeText}
                        </span>
                        <span className="font-medium">{subtype}</span>
                        {tooth && <span className="ml-1.5 text-[11px] text-slate-500">· 牙位 {formatToothPosition(tooth, 999)}</span>}
                      </li>
                    );
                  })}
                </ul>
              </div>
            )}
            {recommendations.length > 0 && (
              <div className="mb-2">
                <div className="text-[11px] text-slate-500 mb-1">医生建议:</div>
                <ul className="space-y-1">
                  {recommendations.map((rec) => {
                    const rc = rec.content as Record<string, unknown>;
                    const text = String(rc.name ?? '') || rec.title || '建议';
                    const tooth = String(rc.tooth_position ?? '');
                    return (
                      <li key={rec.id} className="text-[12px] text-slate-700 leading-relaxed">
                        <span className="px-1.5 py-px mr-1.5 bg-amber-50 text-amber-700 rounded text-[10.5px] font-medium">建议</span>
                        <span className="font-medium">{text}</span>
                        {tooth && <span className="ml-1.5 text-[11px] text-slate-500">· 牙位 {formatToothPosition(tooth, 999)}</span>}
                      </li>
                    );
                  })}
                </ul>
              </div>
            )}
            {doctorAdvice && (
              <div>
                <div className="text-[11px] text-slate-500 mb-1">医嘱:</div>
                <div className="text-[12px] text-slate-700 leading-relaxed">{doctorAdvice}</div>
              </div>
            )}
          </SoapBlock>
        )}
      </div>
    </section>
  );
}

function SoapBlock({
  letter,
  tone,
  title,
  children,
}: {
  letter: string;
  tone: 'sky' | 'indigo' | 'rose' | 'emerald';
  title: string;
  children: React.ReactNode;
}) {
  const toneCls = {
    sky: 'bg-sky-100 text-sky-700',
    indigo: 'bg-indigo-100 text-indigo-700',
    rose: 'bg-rose-100 text-rose-700',
    emerald: 'bg-emerald-100 text-emerald-700',
  }[tone];
  return (
    <div>
      <div className="flex items-center gap-1.5 mb-1.5">
        <span className={`inline-flex items-center justify-center w-4 h-4 rounded text-[10.5px] font-bold ${toneCls}`}>
          {letter}
        </span>
        <span className="text-[11.5px] font-semibold text-slate-800">{title}</span>
      </div>
      <div className="pl-5">{children}</div>
    </div>
  );
}

/// 解析 host EMR 的 JSON 数组字段(exam_findings / disposal):字符串或 array 都接受
function parseJsonArray(raw: unknown): Array<{ toothPosition?: string; message?: string }> {
  if (Array.isArray(raw)) return raw as Array<{ toothPosition?: string; message?: string }>;
  if (typeof raw !== 'string' || !raw.trim() || raw === 'null') return [];
  try {
    const parsed = JSON.parse(raw);
    if (Array.isArray(parsed)) return parsed;
    return [];
  } catch {
    return [];
  }
}

/// 牙位紧凑显示(全口压缩)— UI 已有 formatToothPosition 但本组件场景更紧凑
