// 伏笔看板纯逻辑：四泳道分组、登记/转移请求体组装、422 reason 映射。
// 对齐 C3 扩（GET/POST/PATCH .../foreshadow）。纯逻辑，便于 node 环境单测。

import type {
  ForeshadowRegisterRequest,
  ForeshadowTransitionRequest,
  ForeshadowView,
} from "@/lib/api/types";

// 四状态泳道（UX §6.8）；OVERDUE 用琥珀强调（渲染层处理）。
export type ForeshadowStatus = "OPEN" | "PARTIAL" | "CLOSED" | "OVERDUE";

export const LANES: readonly ForeshadowStatus[] = [
  "OPEN",
  "PARTIAL",
  "CLOSED",
  "OVERDUE",
] as const;

export const LANE_LABELS: Record<ForeshadowStatus, string> = {
  OPEN: "待推进",
  PARTIAL: "推进中",
  CLOSED: "已回收",
  OVERDUE: "已逾期",
};

export type ForeshadowLanes = Record<ForeshadowStatus, ForeshadowView[]>;
export type ForeshadowLaneCounts = Record<ForeshadowStatus, number>;

function emptyLanes(): ForeshadowLanes {
  return { OPEN: [], PARTIAL: [], CLOSED: [], OVERDUE: [] };
}

function isStatus(v: string): v is ForeshadowStatus {
  return v === "OPEN" || v === "PARTIAL" || v === "CLOSED" || v === "OVERDUE";
}

// 按 status 分四泳道（未知 status 落 OPEN 兜底）；保持 repo 已排序的 code 顺序。
export function groupByStatus(
  items: readonly ForeshadowView[] | undefined,
): ForeshadowLanes {
  const lanes = emptyLanes();
  for (const item of items ?? []) {
    const status = isStatus(item.status) ? item.status : "OPEN";
    lanes[status] = [...lanes[status], item];
  }
  return lanes;
}

export function countByStatus(
  items: readonly ForeshadowView[] | undefined,
): ForeshadowLaneCounts {
  const lanes = groupByStatus(items);
  return {
    OPEN: lanes.OPEN.length,
    PARTIAL: lanes.PARTIAL.length,
    CLOSED: lanes.CLOSED.length,
    OVERDUE: lanes.OVERDUE.length,
  };
}

// 接近回收窗口判定：current 在 [from, to] 内，或已超 from 但尚未 CLOSED（提示安排回收）。
// 仅对 OPEN/PARTIAL 提示；CLOSED 已回收、OVERDUE 已逾期另有强调。
export function isWindowApproaching(
  item: ForeshadowView,
  currentChapter: number,
): boolean {
  if (item.status === "CLOSED" || item.status === "OVERDUE") return false;
  const to = item.expected_close_to;
  if (typeof to !== "number") return false;
  const from = item.expected_close_from ?? to;
  return currentChapter >= from && currentChapter <= to;
}

// 为「新埋待确认」建议预填一个建议代号：优先用建议自带 code，否则 FS-<两位序号>。
// 纯函数（可单测）；作者可在登记表单里改。
export function suggestForeshadowCode(
  code: string | null | undefined,
  index: number,
): string {
  const trimmed = code?.trim();
  if (trimmed) return trimmed;
  return `FS-${String(index + 1).padStart(2, "0")}`;
}

// 在已占用代号（taken，区分大小写按原样）中，为预填代号挑一个不冲突的值：
// preferred 未被占用就用它；否则从 FS-01 起取首个空闲的 FS-<两位序号>。
// 纯函数（可单测）。避免登记时与库里已存代号撞码 → 422 duplicate。
export function nextFreeForeshadowCode(
  preferred: string,
  taken: ReadonlySet<string>,
): string {
  if (preferred && !taken.has(preferred)) return preferred;
  for (let n = 1; n < 1000; n++) {
    const candidate = `FS-${String(n).padStart(2, "0")}`;
    if (!taken.has(candidate)) return candidate;
  }
  return preferred; // 理论不可达：1000 个代号全占用时退回原值
}

export interface RegisterInput {
  projectId: string;
  code: string;
  title: string;
  plantedAt?: number | null;
  content?: string | null;
  expectedCloseFrom?: number | null;
  expectedCloseTo?: number | null;
  importance?: string | null;
}

// 组装登记请求体（去空白；空可选字段省略）。
export function buildRegisterRequest(
  input: RegisterInput,
): ForeshadowRegisterRequest {
  const body: ForeshadowRegisterRequest = {
    code: input.code.trim(),
    title: input.title.trim(),
  };
  if (typeof input.plantedAt === "number") body.planted_at = input.plantedAt;
  const content = input.content?.trim();
  if (content) body.content = content;
  if (typeof input.expectedCloseFrom === "number")
    body.expected_close_from = input.expectedCloseFrom;
  if (typeof input.expectedCloseTo === "number")
    body.expected_close_to = input.expectedCloseTo;
  const importance = input.importance?.trim();
  if (importance) body.importance = importance;
  return body;
}

export interface TransitionInput {
  projectId: string;
  toStatus?: ForeshadowStatus | null;
  // 一条进展记录（自由 dict；如 {chapter, note}）。
  progressEntry?: Record<string, unknown> | null;
}

// 组装转移/进展请求体（两字段皆可选；至少给一项由调用方校验）。
export function buildTransitionRequest(
  input: TransitionInput,
): ForeshadowTransitionRequest {
  const body: ForeshadowTransitionRequest = {};
  if (input.toStatus) body.to_status = input.toStatus;
  if (input.progressEntry && Object.keys(input.progressEntry).length > 0)
    body.progress_entry = input.progressEntry;
  return body;
}

// 422 VALIDATION 信封 details.reason（C3 扩）→ 用户文案。
export type ValidationReason =
  | "duplicate"
  | "invalid_transition"
  | "empty_update"
  | "invalid_status";

export function validationReasonMessage(reason: string | undefined): string {
  switch (reason) {
    case "duplicate":
      return "该伏笔代号已存在，请换一个。";
    case "invalid_transition":
      return "非法状态转移（CLOSED 为终态，不可再改）。";
    case "empty_update":
      return "请填写目标状态或一条进展。";
    case "invalid_status":
      return "无效的筛选状态。";
    default:
      return "操作未通过校验，请检查输入。";
  }
}

// 从错误信封安全提取 details.reason。
export function extractReason(error: unknown): string | undefined {
  if (typeof error !== "object" || error === null) return undefined;
  const env = error as {
    error?: { details?: { reason?: unknown } | null };
  };
  const reason = env.error?.details?.reason;
  return typeof reason === "string" ? reason : undefined;
}
