// 文风纯逻辑：指纹归一（dims+evidence）、漂移归一（从 chapter_reviews.style）、
// 学文风/回炉请求体组装。纯逻辑，便于 node 环境单测（C3 扩 T4.3 / C4 扩 T4.2）。

import type {
  ReviewHistoryItem,
  StyleFingerprintResponse,
  StyleLearnRequest,
  RefineRequest,
} from "@/lib/api/types";
import type { StyleDriftReport, StyleDriftSegment } from "@/lib/review/sse";

export type { StyleDriftReport, StyleDriftSegment } from "@/lib/review/sse";

// ── 指纹（16 维 + 证据，UX §6.9）─────────────────────────────────
// 后端 dimensions: DimensionEntry[]（每条 {name,value,evidence}，强类型，替代弱类型 dict）。
export interface FingerprintDimension {
  name: string;
  value: string;
  evidence: string[];
}

export interface Fingerprint {
  dimensions: FingerprintDimension[];
  version: number;
}

// 把 GET /style 响应映射成 Fingerprint。
// dimensions 数组顺序即维度顺序（身份）；每条已携带 name/value/evidence。
export function normalizeFingerprint(
  resp: StyleFingerprintResponse | undefined,
): Fingerprint | null {
  if (!resp) return null;
  const dimensions: FingerprintDimension[] = (resp.dimensions ?? []).map(
    (d) => ({
      name: d.name,
      value: d.value,
      evidence: d.evidence ?? [],
    }),
  );
  return { dimensions, version: resp.version };
}

// ── 漂移（第四审，C4 扩 T4.2）────────────────────────────────────
// chapter_reviews.style = {score:int, segments:[{idx,score,label?}]}。
// 类型 StyleDriftReport/StyleDriftSegment 在 lib/review/sse.ts（reduce 复用），上面已 re-export。

function asInt(v: unknown, fallback = 0): number {
  return typeof v === "number" && Number.isFinite(v) ? Math.round(v) : fallback;
}

function asOptionalString(v: unknown): string | null {
  return typeof v === "string" ? v : null;
}

function asString(v: unknown): string {
  return typeof v === "string" ? v : "";
}

// 把松散 style dict 收窄成漂移报告；缺失/非 dict → null（不渲染）。
export function normalizeStyleDrift(
  item: ReviewHistoryItem | undefined,
): StyleDriftReport | null {
  const raw = item?.style;
  if (typeof raw !== "object" || raw === null) return null;
  const dict = raw as Record<string, unknown>;
  const segRaw = Array.isArray(dict["segments"]) ? dict["segments"] : [];
  const segments: StyleDriftSegment[] = segRaw
    .filter((s): s is Record<string, unknown> => typeof s === "object" && s !== null)
    .map((s) => ({
      idx: asInt(s["idx"]),
      text: asString(s["text"]),
      score: asInt(s["score"], 100),
      label: asOptionalString(s["label"]),
    }));
  // score 缺省 100（无指纹降级态，对齐后端默认）。
  return { score: asInt(dict["score"], 100), segments };
}

// 同 style SSE 事件 data 也用此收窄（reduceReview case "style" 复用）。
export function narrowStyleEvent(data: unknown): StyleDriftReport {
  if (typeof data !== "object" || data === null) {
    return { score: 100, segments: [] };
  }
  const dict = data as Record<string, unknown>;
  const segRaw = Array.isArray(dict["segments"]) ? dict["segments"] : [];
  const segments: StyleDriftSegment[] = segRaw
    .filter((s): s is Record<string, unknown> => typeof s === "object" && s !== null)
    .map((s) => ({
      idx: asInt(s["idx"]),
      text: asString(s["text"]),
      score: asInt(s["score"], 100),
      label: asOptionalString(s["label"]),
    }));
  return { score: asInt(dict["score"], 100), segments };
}

// ── 请求体组装 ──────────────────────────────────────────────────
export type StyleLearnMode = "create" | "update";

// 组装学文风请求体：去掉空白样本；mode 透传（首学/更新仅前端语义）。
export function buildLearnRequest(
  samples: readonly string[],
  mode: StyleLearnMode,
): StyleLearnRequest {
  const cleaned = samples.map((s) => s.trim()).filter((s) => s.length > 0);
  return { samples: cleaned, mode };
}

// 至少一条非空样本才可提交（对齐后端 min_length=1）。
export function hasUsableSamples(samples: readonly string[]): boolean {
  return samples.some((s) => s.trim().length > 0);
}

// 组装回炉请求体（段去空白；空指令省略）。
export function buildRefineRequest(
  segment: string,
  instruction?: string | null,
): RefineRequest {
  const body: RefineRequest = { segment: segment.trim() };
  const trimmed = instruction?.trim();
  if (trimmed) body.instruction = trimmed;
  return body;
}
