/**
 * 把后端 /pac/v1/plans/:id/full 返回的真实数据 → 适配成 plan-detail-app 期望的形态。
 *
 * AI 产物(script / summaries)首次加载用 mock 兜底,客服点"重新生成"才真调 AI。
 * 其他都用真实 DB 数据。
 */
import { ExecutionChannel, UserRole, personaFeatureMeta, planScenarioLabel } from '@pac/types';
import { fmtRel as sharedFmtRel } from '@pac/utils/format';
import type { Chain, PersonaFeature, PlanReason } from './mock-data';
import { mockScript, mockSummaries } from './mock-data';
import type { PlanDetailData } from './plan-detail-types';
import type { TokenDictionary } from '@pac/types';

/// 诊所 id → 名:有字典且命中 → 名;否则回退原始 id(目标诊所可能不在用户权限/字典内)
function clinicName(id: string | null | undefined, dict?: TokenDictionary): string | null {
  if (!id) return null;
  return dict?.clinics?.[id] ?? id;
}

export function adaptData(real: PlanDetailData, dict?: TokenDictionary) {
  const now = new Date();
  const fmtRel = (d: Date) => sharedFmtRel(d, now);

  // patient
  const patient = {
    id: real.patient.id,
    externalId: real.patient.externalId,
    name: real.patient.name ?? '(未知)',
    nameMasked: real.patient.nameMasked ?? '*',
    gender: real.patient.gender ?? '—',
    age: real.patient.age ?? null, // 生日缺失保留 null(渲染 "?" 岁),不要落成 0 岁
    birthDate: real.patient.birthDate ?? '',
    phone: real.patient.phone ?? '',
    phoneMasked: real.patient.phoneMasked ?? '',
    tags: real.patient.tags.length > 0 ? real.patient.tags : ['普通患者'],
    preferences: {
      contactWindow: '19:00–21:30', // 还没数据源
      preferredChannel: ExecutionChannel.PHONE,
      language: '普通话',
    },
    profile: {
      doNotContact: real.profile?.doNotContact ?? false,
      deceased: real.profile?.deceased ?? false,
      // host 没给 → null,UI 用 '—' 占位;不再前端硬兜底 '本人'(误导客服以为是本人)
      primaryContactType: real.profile?.primaryContactType ?? null,
      ltv: Math.round((real.profile?.ltvCents ?? 0) / 100),
      firstVisit: real.profile?.firstVisit ?? '',
      lastVisit: real.profile?.lastVisit ?? '',
    },
  };

  // chains
  const chains: Chain[] = real.chains.map((c) => ({
    id: c.id,
    name: c.name,
    category: c.category,
    status: c.status,
    currentStage: c.currentStage as Chain['currentStage'],
    value: c.estimatedValueCents ? undefined : 0,
    estimatedValue: c.estimatedValueCents ? Math.round(c.estimatedValueCents / 100) : undefined,
    target: c.target,
    diagnosedAt: c.diagnosedAt,
    gapDays: c.gapDays,
    latestDxAt: c.latestDxAt,
    latestDxName: c.latestDxName,
    latestDxAdvice: c.latestDxAdvice,
    lifecycleNoteZh: c.lifecycleNoteZh,
    toothPosition: c.toothPosition,
    alternativeClosedBy: c.alternativeClosedBy,
    nodes: c.nodes.map((n) => ({
      stage: n.stage as 1 | 2 | 3 | 4 | 5,
      title: n.title,
      detail: n.detail,
      doctor: n.doctor,
      at: n.at,
      hint: n.hint,
      done: n.done,
      current: n.current,
      missing: n.missing,
      // 旧字段兼容透传(老调用方)
      label: n.label,
      note: n.note,
    })),
  }));

  // persona
  const persona = {
    id: real.persona?.id ?? '—',
    version: real.persona?.version ?? 0,
    computedAt: real.persona ? new Date(real.persona.computedAt) : now,
    features: (real.persona?.features ?? []).map((f) => {
      const meta = personaFeatureMeta(f.key);
      return {
        key: f.key,
        label: meta.label,
        value: f.description,
        tone: meta.tone,
        evidence: [],
      } as PersonaFeature;
    }),
  };

  // plan
  const planReasons: PlanReason[] = (real.plan?.reasons ?? []).map((r) => ({
    id: r.id,
    scenario: r.scenario,
    subKey: r.subKey,
    signals: r.signals as PlanReason['signals'],
    scenarioLabel: planScenarioLabel(r.scenario),
    priorityScore: r.priorityScore,
    lifecycle: r.lifecycle as 'one_shot' | 'recurring',
    source: r.source as PlanReason['source'],
    reason: r.reason,
    evidence: (r.evidence?.factIds ?? []).map((id) => {
      const f = real.facts.find((x) => x.id === id);
      return {
        id,
        label: f?.title ?? id,
        source: f?.type ?? '—',
        occurredAt: f?.occurredAt?.slice(0, 10) ?? '—',
      };
    }),
    /// v2.1 6 因子 breakdown 透传给详情页"为什么 X 分"组件
    breakdown: r.breakdown,
  }));

  const plan = {
    id: real.plan?.id ?? '',
    version: real.plan?.version ?? 0,
    status: real.plan?.status ?? 'active',
    priorityScore: real.plan?.priorityScore ?? 0,
    contactAttempts: real.plan?.contactAttempts ?? 0,
    maxContactAttempts: real.plan?.maxContactAttempts ?? 4,
    targetClinic: clinicName(real.plan?.targetClinicId, dict) ?? '(集团统一池)',
    executorClinic: clinicName(real.plan?.targetClinicId, dict) ?? '(集团统一池)',
    goal: real.plan?.goal ?? null,
    assignee: {
      id: real.plan?.assigneeUserId ?? 'u_unknown',
      name: real.plan?.assigneeUserId ?? '(未分配)',
      role: (real.plan?.recommendedRole as UserRole) ?? UserRole.STAFF,
    },
    assignedAt: real.plan?.assignedAt ? new Date(real.plan.assignedAt) : now,
    recycleAt: real.plan?.recycleAt ? new Date(real.plan.recycleAt) : null,
    snoozedUntil: real.plan?.snoozedUntil ? new Date(real.plan.snoozedUntil) : null,
    updatedAt: real.plan?.updatedAt ? new Date(real.plan.updatedAt) : null,
    recommendedAt: real.plan?.recommendedAt ? new Date(real.plan.recommendedAt) : now,
    recommendedRole: (real.plan?.recommendedRole as UserRole) ?? UserRole.STAFF,
    recommendedChannel: (real.plan?.recommendedChannel as ExecutionChannel) ?? ExecutionChannel.PHONE,
    reasons: planReasons,
  };

  // ⭐ v2.1:facts 透出给 FactsTimeline / ImageDrawer
  const facts = real.facts.map((f) => ({
    id: f.id,
    type: f.type,
    kind: f.kind,
    status: f.status,
    occurredAt: f.occurredAt ?? null,
    plannedFor: f.plannedFor ?? null,
    clinicId: f.clinicId ?? null,
    title: f.title ?? null,
    summary: f.summary ?? null,
    content: f.content as Record<string, unknown> | null,
  }));

  return {
    patient,
    chains,
    persona,
    plan,
    facts,
    summaries: mockSummaries, // AI 假数据 — 接入 Vercel AI SDK 时换
    // W4:话术 DB 持久化 — LLM 生成完会落 plan_scripts;real.script 命中即用真实数据
    // 没生成过(real.script=null) → mock 兜底,客服点"重新生成"触发首次生成
    // shape 跟 mockScript 完全一致(status/source/generatedAt/sections),用 as 转把后端宽 string 收窄到 mock 字面量类型
    script: real.script && real.script.status === 'ready'
      ? ({
          status: 'ready',
          source: real.script.source,
          generatedAt: new Date(real.script.generatedAt),
          sections: real.script.sections,
        } as typeof mockScript)
      : mockScript,
    // 召回历史(患者级)— 后端 plan-aggregate 透出;无则空数组
    recallHistory: real.recallHistory ?? [],
    // outcomeOptions 已迁移到 @pac/types EXECUTION_OUTCOME_META,outcome-form 直接 import
    fmtRel,
  };
}

/// v2.1 facts 透传给 FactsTimeline / ImageDrawer 使用的类型
export type AdaptedFact = {
  id: string;
  type: string;
  kind: string;
  status: string;
  occurredAt: string | null;
  plannedFor: string | null;
  clinicId: string | null;
  title: string | null;
  summary: string | null;
  content: Record<string, unknown> | null;
};
