'use client';

import * as React from 'react';
import {
  PERSONA_FEATURE_CATEGORY_META,
  PERSONA_FEATURE_CATEGORY_ORDER,
  personaFeatureCategory,
  personaFeatureSortKey,
  type PersonaFeatureCategory,
} from '@pac/types';
import { cn } from '@/lib/utils';
import { tone } from './shared';
import { PersonaFeatureHover } from './persona-feature-hover';
import { factLabel } from './fact-label';
import { personaValueLabels } from './persona-display';
import type { PersonaFeature } from './mock-data';
import type { AdaptedFact } from './adapt-data';

/**
 * 画像标签详情列表(抽屉体)。
 *
 * 相对旧版改了三件事:
 *  ① **分组 + 定序**。原来 16 张卡等权重平铺,顺序来自 `orderBy createdAt`;而同一版的 feature
 *     行是一条 createMany 写进去的、createdAt 完全相同 → 排序实际未定义,生产上呈现为英文 key
 *     字母序:「急迫等级 = 紧急」排在最后一个,「性别」「获客渠道」排在最前。
 *     现按**业务字典的 A/B/C/D 类区**分组(2026-07-30 起,与身份卡首屏 chip 同出一源;
 *     此前这里是另一套「跟进要点 / 价值与阶段 / 基础属性」三分组,两套分类并存必然漂)。
 *  ② **给出依据**。后端一直存着 evidence.factIds(rfm + lifecycle 两个特征在生产就占 1 GB),
 *     但前端 adapt-data 把它写死成空数组 —— 客服只看得到结论、看不到出处。现在反查同一份 facts
 *     渲染最近几条,并给出总条数。
 *  ③ **`?` 改成真按钮**。原来是 16×16 的 `<span>`,无 tabindex 无 role、纯 hover —— 键盘、读屏、
 *     平板触屏都够不着。现在是 button(focus 可触发 hover 卡),热区放大到 24×24。
 */
const MAX_EVIDENCE_ROWS = 4;

/**
 * 折扣锚点超过这个年数就提示「已过时」。
 *
 * 生产上见过 2018 年的锚点(8 年前)照旧原样展示 —— 客服照着那个折扣去谈会出事。
 * 阈值取 3 年:口腔项目定价与集团政策的调整周期大致在这个量级。
 */
const DISCOUNT_ANCHOR_STALE_YEARS = 3;

/** 折扣锚点的过时提醒;非折扣锚点或日期缺失 → null */
function staleAnchorHint(feature: PersonaFeature): string | null {
  if (feature.key !== 'discount_anchor') return null;
  const at = (feature.data as { anchorDate?: unknown } | null | undefined)?.anchorDate;
  if (typeof at !== 'string') return null;
  const years = (Date.now() - new Date(at).getTime()) / (365.25 * 86400_000);
  if (!Number.isFinite(years) || years < DISCOUNT_ANCHOR_STALE_YEARS) return null;
  return `该折扣发生在 ${Math.floor(years)} 年前,当时的定价与政策未必仍适用 —— 报价前请先与主管确认`;
}

export function PersonaDetailList({
  features,
  facts,
  focusKey,
}: {
  features: PersonaFeature[];
  facts: AdaptedFact[];
  /**
   * 打开时要定位到的标签(身份卡首屏 chip 点进来带的)。
   * 抽屉里十几张卡、要找的那张常在折叠线以下 —— 不滚过去等于让人自己翻,
   * 那点开标签跟点「详情 →」就没区别了。
   */
  focusKey?: string | null;
}) {
  const factById = React.useMemo(() => new Map(facts.map((f) => [f.id, f])), [facts]);

  // 滚到目标卡。⚠️ 必须等一帧:抽屉是本次渲染才挂上的,同步滚时容器还没有布局,scrollIntoView 是空操作。
  const focusRef = React.useRef<HTMLDivElement | null>(null);
  React.useEffect(() => {
    if (!focusKey) return;
    const id = requestAnimationFrame(() =>
      focusRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' }),
    );
    return () => cancelAnimationFrame(id);
  }, [focusKey]);

  const grouped = React.useMemo(() => {
    const sorted = [...features].sort((a, b) => {
      const [ga, oa] = personaFeatureSortKey(a.key);
      const [gb, ob] = personaFeatureSortKey(b.key);
      return ga - gb || oa - ob || a.key.localeCompare(b.key);
    });
    const buckets = new Map<PersonaFeatureCategory | 'other', PersonaFeature[]>();
    for (const f of sorted) {
      const c = personaFeatureCategory(f.key) ?? 'other';
      buckets.set(c, [...(buckets.get(c) ?? []), f]);
    }
    return buckets;
  }, [features]);

  // 分组标题带类区色点 —— 与首屏 chip 的颜色一一对应,客服在两处认的是同一套色
  const sections: Array<[string, string, PersonaFeature[]]> = [
    ...PERSONA_FEATURE_CATEGORY_ORDER.map(
      (c) =>
        [PERSONA_FEATURE_CATEGORY_META[c].label, PERSONA_FEATURE_CATEGORY_META[c].tone, grouped.get(c) ?? []] as [
          string,
          string,
          PersonaFeature[],
        ],
    ),
    // 注册表里没登记的标签不吞掉,兜到最后一组(比"标签凭空消失"好排查)
    ['其他', 'slate', grouped.get('other') ?? []],
  ];

  return (
    <div className="space-y-4">
      {sections
        .filter(([, , items]) => items.length > 0)
        .map(([label, sectionTone, items]) => (
          <section key={label} className="space-y-2">
            <h3 className="flex items-center gap-1.5 text-[10.5px] font-semibold tracking-wide text-slate-400">
              <span className={cn('h-1.5 w-1.5 flex-none rounded-full', tone(sectionTone).dot)} />
              {label}
              <span className="font-normal text-slate-300">{items.length}</span>
            </h3>
            {items.map((f) => (
              <FeatureCard
                key={f.key}
                feature={f}
                factById={factById}
                focused={f.key === focusKey}
                cardRef={f.key === focusKey ? focusRef : undefined}
              />
            ))}
          </section>
        ))}
    </div>
  );
}

function FeatureCard({
  feature: f,
  factById,
  focused = false,
  cardRef,
}: {
  feature: PersonaFeature;
  factById: Map<string, AdaptedFact>;
  /** 从首屏 chip 点进来的那张 —— 描边加重,滚到中间后一眼能认出是哪张 */
  focused?: boolean;
  cardRef?: React.RefObject<HTMLDivElement | null>;
}) {
  const [showAll, setShowAll] = React.useState(false);
  const T = tone(f.tone);
  const staleHint = staleAnchorHint(f);

  // 多值特征(治疗史 / 潜在治疗 / 时间偏好…)后端给结构化 data.labels —— 详情里全部展开,
  // 不像卡片那样截成 "+N"。单值特征直接显示后端那句完整 description。
  // ⭐ potential_treatment 走 code 查表(措辞可改),不读烤死的 labels —— 见 persona-display 注释。
  const valueLabels = personaValueLabels(f.data, f.key);

  // 证据:只渲染能在本次响应的 facts 里找到的(跨版本的旧 fact 可能已被 supersede,查不到就不显)
  const resolved = f.evidence
    .map((id) => factById.get(id))
    .filter((x): x is AdaptedFact => !!x)
    .sort((a, b) => (b.occurredAt ?? '').localeCompare(a.occurredAt ?? ''));
  const shown = showAll ? resolved : resolved.slice(0, MAX_EVIDENCE_ROWS);

  return (
    <div
      ref={cardRef}
      className={cn(
        'relative rounded-md border p-3 pr-9',
        focused ? 'border-brand-300 bg-brand-50/40 ring-1 ring-brand-200' : '',
      )}
    >
      {/* 右上角 ? —— 真 button:可 Tab 聚焦,focus 也能唤出说明卡(触屏/键盘可达) */}
      <PersonaFeatureHover featureKey={f.key} value={f.value}>
        <button
          type="button"
          className="absolute top-1.5 right-1.5 inline-flex h-6 w-6 items-center justify-center rounded-full text-slate-400 hover:bg-slate-100 hover:text-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500"
          aria-label={`${f.label} 怎么算的`}
        >
          <svg viewBox="0 0 24 24" className="h-3.5 w-3.5" fill="none" stroke="currentColor" strokeWidth="2">
            <circle cx="12" cy="12" r="10" />
            <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3M12 17h.01" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </button>
      </PersonaFeatureHover>

      {/* 属性名 */}
      <div className={cn('inline-flex items-center gap-1.5 text-[11px] font-semibold', T.text)}>
        <span className={cn('h-1.5 w-1.5 rounded-full', T.dot)} />
        {f.label}
      </div>

      {/* 取值条目 — 多值展开为 chip,单值显示文本 */}
      {valueLabels && valueLabels.length > 0 ? (
        <div className="mt-1.5 flex flex-wrap gap-1">
          {valueLabels.map((l, i) => (
            <span
              key={i}
              className="inline-flex items-center rounded bg-slate-50 px-1.5 py-0.5 text-[12px] font-medium text-slate-800 ring-1 ring-slate-100"
            >
              {l}
            </span>
          ))}
        </div>
      ) : (
        f.value && <div className="mt-1 text-[13px] font-medium text-slate-900">{f.value}</div>
      )}

      {/* 过时提醒 —— 目前只有折扣锚点会触发(见 staleAnchorHint) */}
      {staleHint && (
        <p className="mt-1.5 flex gap-1 rounded bg-amber-50 px-1.5 py-1 text-[10.5px] leading-relaxed text-amber-800">
          <span aria-hidden>⚠️</span>
          <span>{staleHint}</span>
        </p>
      )}

      {/* 依据 —— 这个标签是被哪些事实推出来的 */}
      {resolved.length > 0 && (
        <div className="mt-2 border-t pt-2">
          <div className="mb-1 flex items-baseline gap-2">
            <span className="text-[10.5px] text-slate-400">依据 · {resolved.length} 条</span>
            {resolved.length > MAX_EVIDENCE_ROWS && (
              <button
                type="button"
                onClick={() => setShowAll((v) => !v)}
                className="text-[10.5px] text-brand-700 hover:underline"
              >
                {showAll ? '收起' : `展开全部`}
              </button>
            )}
          </div>
          <ul className="space-y-0.5">
            {shown.map((fact) => {
              const { badge, badgeTone, text: label, hover } = factLabel(fact);
              return (
                <li key={fact.id} className="flex items-baseline gap-1.5 text-[11px] leading-relaxed">
                  {/* 日期不折行 —— 定宽 68px 装不下 `2026-03-27`(11px 字号)会断成两行;
                      改为按内容撑开 + nowrap,tabular-nums 保证各行等宽自然对齐 */}
                  <span className="flex-none whitespace-nowrap tabular-nums text-slate-400">
                    {(fact.occurredAt ?? fact.plannedFor ?? '').slice(0, 10) || '—'}
                  </span>
                  <span className={cn('flex-none rounded px-1 text-[10px]', badgeTone)}>{badge}</span>
                  <span className="min-w-0 flex-1 truncate text-slate-700" title={hover ?? label}>
                    {label}
                  </span>
                </li>
              );
            })}
          </ul>
        </div>
      )}
    </div>
  );
}
