import type {
  AssistantFallbackType,
  AssistantReplyType,
  AnswerGroundingCheck,
  AssistantRequirement,
  RequirementCoverageReport,
  UnifiedRetrievalResult
} from "../types/assistant";
import type { RouteHintSummary } from "../types/normalizer";
import type { AnswerStructureV11, EvidenceConfidence, EvidenceItem, EvidenceLimitationReasonCode } from "../types/stage1Contracts";
import type { ProblemUnit, ProblemUnitSummary, ProblemUnitType } from "../types/stage2ProblemUnits";

type ProblemAnswerMode = "stage1_policy_v11" | "stage2_problem_centric_v1" | "stage3_lifecycle_aware_v1";

interface ComposeAnswerInput {
  userMessage: string;
  routeSummary: RouteHintSummary | null;
  retrievalResults: UnifiedRetrievalResult[];
  requirements: AssistantRequirement[];
  coverageReport: RequirementCoverageReport;
  groundingCheck: AnswerGroundingCheck;
  enableAnswerPolicyV11?: boolean;
  enableProblemCentricAnswerV1?: boolean;
  enableLifecycleAnswerV1?: boolean;
}

interface ComposeAnswerOutput {
  assistant_reply: string;
  fallback_type: AssistantFallbackType;
  reply_type: AssistantReplyType;
  answer_structure_v11?: AnswerStructureV11;
  problem_centric_answer_applied?: boolean;
  problem_units_used_count?: number;
  problem_answer_mode?: ProblemAnswerMode;
  problem_unit_ids_used?: string[];
}

function fallbackFromSummary(routeSummary: RouteHintSummary | null): AssistantFallbackType {
  if (!routeSummary || routeSummary.mode !== "deterministic_v2") {
    return "none";
  }
  return routeSummary.fallback.type as AssistantFallbackType;
}

function uniqueStrings(values: string[], limit = 6): string[] {
  return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean))).slice(0, limit);
}

const UUID_PATTERN = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
const LONG_HEX_PATTERN = /\b[0-9a-f]{24,}\b/gi;
const RAW_REF_BLOB_PATTERN = /\bevidence_source_ref_v1\|[^\s,;]+/gi;
const RAW_REF_TOKEN_PATTERN = /\b(?:source_ref|canonical_ref|entity_id|fragment_id|guid|uuid)\b/gi;
const SYNTHETIC_PLACEHOLDER_PATTERN = /\bunknown_entity(?::[^\s,;]+)?\b/gi;
const SYNTHETIC_FALLBACK_MARKER_PATTERN = /\b(?:unknown_source|unknown_record)\b/gi;
const SYNTHETIC_ROUTE_TOKEN_PATTERN = /\bbatch_refresh_then_store:[^\s,;]+/gi;
const CYRILLIC_MOJIBAKE_FRAGMENT_PATTERN = /(?:[\u0420\u0421][\u0080-\u04FF\u2000-\u20CF]){2,}/u;
const LATIN_MOJIBAKE_FRAGMENT_PATTERN = /(?:[\u00D0\u00D1][\u0080-\u00FF]){2,}/u;
const SHORT_CYRILLIC_MOJIBAKE_TOKEN_PATTERN = /^[\u0420\u0421][\u0080-\u04FF\u2000-\u20CF]{1,2}$/u;
const PREFIXED_SHORT_CYRILLIC_MOJIBAKE_TOKEN_PATTERN = /^[\p{L}\p{N}_-]+[\u0420\u0421][\u0080-\u04FF\u2000-\u20CF]{1,2}$/u;
const MOJIBAKE_SINGLE_MARKER_PATTERN = /^[\u0420\u0421\u00D0\u00D1]$/u;
const MOJIBAKE_MARKER_CHAR_PATTERN = /[\u0402\u0403\u040A\u040C\u040E\u040F\u0452\u0453\u0459\u045A\u045C\u045E\u045F\u201A\u201E\u2020\u2021\u2026\u2030\u20AC\u2122]/u;
const CYRILLIC_MOJIBAKE_FRAGMENT_GLOBAL_PATTERN = /(?:[\u0420\u0421][\u0080-\u04FF\u2000-\u20CF]){2,}/gu;
const LATIN_MOJIBAKE_FRAGMENT_GLOBAL_PATTERN = /(?:[\u00D0\u00D1][\u0080-\u00FF]){2,}/g;
const MOJIBAKE_MARKER_CHAR_GLOBAL_PATTERN = /[\u0402\u0403\u040A\u040C\u040E\u040F\u0452\u0453\u0459\u045A\u045C\u045E\u045F\u201A\u201E\u2020\u2021\u2026\u2030\u20AC\u2122]/gu;

function normalizeToken(value: string): string {
  return value.replace(/^[^\p{L}\p{N}_-]+|[^\p{L}\p{N}_-]+$/gu, "");
}

function isLikelyMojibakeToken(value: string): boolean {
  const token = normalizeToken(String(value ?? ""));
  if (!token) {
    return false;
  }
  if (MOJIBAKE_SINGLE_MARKER_PATTERN.test(token)) {
    return true;
  }
  if (SHORT_CYRILLIC_MOJIBAKE_TOKEN_PATTERN.test(token)) {
    return true;
  }
  if (token.length <= 8 && PREFIXED_SHORT_CYRILLIC_MOJIBAKE_TOKEN_PATTERN.test(token)) {
    return true;
  }
  return CYRILLIC_MOJIBAKE_FRAGMENT_PATTERN.test(token) || LATIN_MOJIBAKE_FRAGMENT_PATTERN.test(token);
}

function countMojibakeTokens(value: string): number {
  return String(value ?? "")
    .split(/[\s,.;:!?()[\]{}"']+/g)
    .filter((token) => token.length > 0)
    .filter((token) => isLikelyMojibakeToken(token)).length;
}

function countMojibakeSingleMarkers(value: string): number {
  return String(value ?? "")
    .split(/[\s,.;:!?()[\]{}"']+/g)
    .filter((token) => token.length > 0)
    .map((token) => normalizeToken(token))
    .filter((token) => MOJIBAKE_SINGLE_MARKER_PATTERN.test(token)).length;
}

function stripMojibakeFragments(value: string): string {
  const removedByToken = String(value ?? "")
    .split(/(\s+)/g)
    .map((part) => {
      if (/^\s+$/u.test(part)) {
        return part;
      }
      return isLikelyMojibakeToken(part) ? "" : part;
    })
    .join("");

  return removedByToken
    .replace(CYRILLIC_MOJIBAKE_FRAGMENT_GLOBAL_PATTERN, "")
    .replace(LATIN_MOJIBAKE_FRAGMENT_GLOBAL_PATTERN, "")
    .replace(MOJIBAKE_MARKER_CHAR_GLOBAL_PATTERN, "")
    .replace(/\s+([,.;:!?])/g, "$1")
    .replace(/\s{2,}/g, " ")
    .trim();
}

function looksLikeMojibake(value: string): boolean {
  const text = String(value ?? "");
  if (!text.trim()) {
    return false;
  }
  const tokenHits = countMojibakeTokens(text);
  const singleMarkers = countMojibakeSingleMarkers(text);
  if (tokenHits >= 2 || (tokenHits >= 1 && singleMarkers >= 1) || singleMarkers >= 3) {
    return true;
  }
  if (MOJIBAKE_MARKER_CHAR_PATTERN.test(text)) {
    return true;
  }
  if (CYRILLIC_MOJIBAKE_FRAGMENT_PATTERN.test(text) || LATIN_MOJIBAKE_FRAGMENT_PATTERN.test(text)) {
    return true;
  }
  if (/\uFFFD/u.test(text)) {
    return true;
  }
  return false;
}
function looksLikeTechnicalIdentifier(value: string): boolean {
  const text = String(value ?? "").trim();
  if (!text) {
    return false;
  }
  if (UUID_PATTERN.test(text)) {
    UUID_PATTERN.lastIndex = 0;
    return true;
  }
  UUID_PATTERN.lastIndex = 0;
  if (LONG_HEX_PATTERN.test(text)) {
    LONG_HEX_PATTERN.lastIndex = 0;
    return true;
  }
  LONG_HEX_PATTERN.lastIndex = 0;
  return /(?:evidence_source_ref_v1\||cmp%3a|batch_refresh_then_store:|^cmp:)/i.test(text);
}

function scrubRawTechnicalRefs(value: string): string {
  const raw = String(value ?? "").trim();
  if (!raw) {
    return "";
  }
  return raw
    .replace(RAW_REF_BLOB_PATTERN, "linked source")
    .replace(UUID_PATTERN, "[id]")
    .replace(LONG_HEX_PATTERN, "[id]")
    .replace(RAW_REF_TOKEN_PATTERN, "reference")
    .replace(/\(\s*\[id\]\s*\)/g, "")
    .replace(/\[\s*id\s*\](?:\s*,\s*\[\s*id\s*\])+/g, "[id]")
    .replace(/\s{2,}/g, " ")
    .trim();
}

function stripSyntheticPlaceholders(value: string): string {
  return String(value ?? "")
    .replace(SYNTHETIC_PLACEHOLDER_PATTERN, "")
    .replace(SYNTHETIC_FALLBACK_MARKER_PATTERN, "")
    .replace(SYNTHETIC_ROUTE_TOKEN_PATTERN, "")
    .replace(/[;,:]\s*[;,:]+/g, "; ")
    .replace(/\s{2,}/g, " ")
    .trim();
}

function sanitizeUserFacingReply(value: string): string {
  const normalized = scrubRawTechnicalRefs(value).replace(/[ \t]+\n/g, "\n");
  const cleanedLines = normalized
    .split(/\r?\n/g)
    .map((line) => stripSyntheticPlaceholders(line))
    .map((line) => stripMojibakeFragments(line))
    .map((line) => line.trim())
    .filter((line) => line.length > 0)
    .filter((line) => !looksLikeMojibake(line));
  const cleaned = cleanedLines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
  return cleaned || "Available data requires clarification for a reliable user-facing answer.";
}

function sanitizeUserText(value: string): string | null {
  const normalized = stripMojibakeFragments(
    stripSyntheticPlaceholders(scrubRawTechnicalRefs(String(value ?? "").replace(/\s+/g, " ").trim()))
  );
  if (!normalized) {
    return null;
  }
  if (looksLikeMojibake(normalized)) {
    return null;
  }
  return normalized;
}

function sanitizeUserLines(values: string[], limit = 6): string[] {
  const cleaned = values
    .map((item) => sanitizeUserText(item))
    .filter((item): item is string => Boolean(item));
  return uniqueStrings(cleaned, limit);
}

function formatList(items: string[]): string {
  if (items.length === 0) {
    return "";
  }
  return items.map((item) => `- ${item}`).join("\n");
}

function formatSafeItemLine(entity: unknown, sourceId: unknown, riskScore?: unknown): string {
  const entityLabel = sanitizeUserText(String(entity ?? "")) ?? "Record";
  const idRaw = String(sourceId ?? "").trim();
  const exposeId = idRaw.length > 0 && !looksLikeTechnicalIdentifier(idRaw);
  const subject = exposeId ? `${entityLabel} (${idRaw})` : entityLabel;
  if (riskScore !== undefined) {
    return `${subject} - risk ${String(riskScore)}.`;
  }
  return `${subject}.`;
}

function extractTopFacts(results: UnifiedRetrievalResult[]): string[] {
  const lines: string[] = [];
  for (const result of results.filter((item) => item.status === "ok").slice(0, 3)) {
    if (result.result_type === "chain") {
      const top = result.items.slice(0, 3).map((item) => {
        const counterparty = String(item.counterparty_id ?? "").trim();
        const operations = String(item.operations_count ?? "0");
        const docs = String(item.document_refs_count ?? "0");
        const counterpartyLabel =
          counterparty.length > 0 && !looksLikeTechnicalIdentifier(counterparty) ? `Counterparty ${counterparty}` : "Counterparty";
        return `${counterpartyLabel}: operations ${operations}, linked docs ${docs}.`;
      });
      lines.push(...top);
      continue;
    }
    if (result.result_type === "ranking") {
      const top = result.items
        .slice(0, 5)
        .map((item) => `${item.rank ?? "*"}. ${String(item.entity ?? "Entity")} - ${String(item.records_count ?? 0)}.`);
      lines.push(...top);
      continue;
    }
    if (result.result_type === "list") {
      const top = result.items.slice(0, 5).map((item) => {
        if (item.risk_score !== undefined) {
          return formatSafeItemLine(item.source_entity ?? "Record", item.source_id ?? "", item.risk_score);
        }
        return formatSafeItemLine(item.source_entity ?? "Record", item.source_id ?? "");
      });
      lines.push(...top);
      continue;
    }
    const top = result.items
      .slice(0, 3)
      .map((item) => formatSafeItemLine(item.source_entity ?? "Record", item.source_id ?? ""));
    lines.push(...top);
  }
  return sanitizeUserLines(lines, 8);
}

function extractWhyIncluded(results: UnifiedRetrievalResult[]): string[] {
  return sanitizeUserLines(results.flatMap((item) => item.why_included));
}

function extractSelectionReasons(results: UnifiedRetrievalResult[]): string[] {
  return sanitizeUserLines(results.flatMap((item) => item.selection_reason));
}

function extractRiskFactors(results: UnifiedRetrievalResult[]): string[] {
  return sanitizeUserLines(results.flatMap((item) => item.risk_factors));
}

function extractBusinessInterpretation(results: UnifiedRetrievalResult[]): string[] {
  return sanitizeUserLines(results.flatMap((item) => item.business_interpretation));
}

function extractLimitations(results: UnifiedRetrievalResult[]): string[] {
  return sanitizeUserLines(results.flatMap((item) => item.limitations), 10);
}

function summaryValue(result: UnifiedRetrievalResult, key: string): unknown {
  const summary = result.summary ?? {};
  return Object.prototype.hasOwnProperty.call(summary, key) ? summary[key] : undefined;
}

function summaryBoolean(result: UnifiedRetrievalResult, key: string): boolean {
  return summaryValue(result, key) === true;
}

function summaryString(result: UnifiedRetrievalResult, key: string): string | null {
  const value = summaryValue(result, key);
  return typeof value === "string" ? value : null;
}

function summaryNumber(result: UnifiedRetrievalResult, key: string): number | null {
  const value = summaryValue(result, key);
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}

function summaryStringArray(result: UnifiedRetrievalResult, key: string): string[] {
  const value = summaryValue(result, key);
  if (!Array.isArray(value)) {
    return [];
  }
  return sanitizeUserLines(value.map((item) => String(item)), 6);
}

function buildFallbackWhyIncluded(results: UnifiedRetrievalResult[]): string[] {
  const lines: string[] = [];
  for (const result of results.slice(0, 2)) {
    const routeFocus = summaryString(result, "route_focus");
    const sourceRecords = summaryNumber(result, "source_records");
    const filteredRecords = summaryNumber(result, "filtered_records_after_narrowing");
    const checkedRecords = summaryNumber(result, "checked_records");

    if (routeFocus) {
      lines.push(`РџСЂРѕРІРµСЂРєР° РІС‹РїРѕР»РЅРµРЅР° РїРѕ РїСЂРѕС„РёР»СЋ ${routeFocus}.`);
    }
    if (sourceRecords !== null && filteredRecords !== null && filteredRecords < sourceRecords) {
      lines.push(`РџСЂРёРјРµРЅРµРЅРѕ СЃСѓР¶РµРЅРёРµ РІС‹Р±РѕСЂРєРё: ${filteredRecords} РёР· ${sourceRecords} Р·Р°РїРёСЃРµР№.`);
    }
    if (checkedRecords !== null) {
      lines.push(`РџСЂРѕРІРµСЂРµРЅРѕ Р·Р°РїРёСЃРµР№ РІ С‚РµРєСѓС‰РµРј РїСЂРѕС…РѕРґРµ: ${checkedRecords}.`);
    }
  }

  return sanitizeUserLines(lines, 4);
}

function buildFallbackSelectionReasons(results: UnifiedRetrievalResult[]): string[] {
  const lines: string[] = [];
  for (const result of results.slice(0, 2)) {
    if (summaryBoolean(result, "semantic_narrowing_applied")) {
      lines.push("РћС‚Р±РѕСЂ РІС‹РїРѕР»РЅРµРЅ РїРѕ СЃРµРјР°РЅС‚РёС‡РµСЃРєРѕРјСѓ СЃСѓР¶РµРЅРёСЋ РїСЂРµРґРјРµС‚РЅРѕР№ РѕР±Р»Р°СЃС‚Рё.");
    }
    const rankingBasis = summaryStringArray(result, "ranking_basis");
    if (rankingBasis.length > 0) {
      lines.push(`Р Р°РЅР¶РёСЂРѕРІР°РЅРёРµ РѕСЃРЅРѕРІР°РЅРѕ РЅР°: ${rankingBasis.join(", ")}.`);
    }
    if (summaryBoolean(result, "broad_guard_applied")) {
      lines.push("РџСЂРёРјРµРЅРµРЅ broad-query guard РґР»СЏ РєРѕРЅС‚СЂРѕР»СЏ Р»РѕР¶РЅРѕР№ С‚РѕС‡РЅРѕСЃС‚Рё.");
    }
  }

  if (lines.length === 0) {
    lines.push("РћС‚Р±РѕСЂ РІС‹РїРѕР»РЅРµРЅ РїРѕ СЃРѕРІРїР°РґРµРЅРёСЋ РїСЂРµРґРјРµС‚РЅС‹С… СЃРёРіРЅР°Р»РѕРІ Рё РґРѕСЃС‚СѓРїРЅРѕР№ evidence-РѕРїРѕСЂС‹.");
  }

  return sanitizeUserLines(lines, 4);
}

function suggestNextStep(requirements: AssistantRequirement[], coverage: RequirementCoverageReport): string[] {
  const next: string[] = [];
  if (coverage.clarification_needed_for.length > 0) {
    next.push("РЈС‚РѕС‡РЅРёС‚Рµ РїРµСЂРёРѕРґ, СЃС‡РµС‚, РґРѕРєСѓРјРµРЅС‚ РёР»Рё РєРѕРЅС‚СЂР°РіРµРЅС‚Р° РґР»СЏ С‚СЂРµР±РѕРІР°РЅРёР№: " + coverage.clarification_needed_for.join(", ") + ".");
  }
  if (coverage.requirements_uncovered.length > 0) {
    next.push("РџСЂРѕРІРµСЂСЊС‚Рµ РЅРµРїРѕРєСЂС‹С‚С‹Рµ С‚СЂРµР±РѕРІР°РЅРёСЏ: " + coverage.requirements_uncovered.join(", ") + ".");
  }
  if (coverage.out_of_scope_requirements.length > 0) {
    next.push("Р§Р°СЃС‚СЊ Р·Р°РїСЂРѕСЃР° РІРЅРµ С‚РµРєСѓС‰РµРіРѕ СѓС‡РµС‚РЅРѕРіРѕ РєРѕРЅС‚СѓСЂР°: " + coverage.out_of_scope_requirements.join(", ") + ".");
  }
  if (next.length === 0 && requirements.length > 0) {
    next.push("РЎР»РµРґСѓСЋС‰РёРј С€Р°РіРѕРј РјРѕР¶РЅРѕ РѕС‚РєСЂС‹С‚СЊ С‚РµС…РЅРёС‡РµСЃРєРёР№ СЂР°Р·Р±РѕСЂ Рё СѓРіР»СѓР±РёС‚СЊ РїСЂРѕРІРµСЂРєСѓ РїРѕ РІС‹Р±СЂР°РЅРЅС‹Рј РѕР±СЉРµРєС‚Р°Рј.");
  }
  return next;
}

interface PolicySignals {
  broad_query_detected: boolean;
  broad_result_flag: boolean;
  minimum_evidence_failed: boolean;
  degraded_to: "partial" | "clarification" | null;
  narrowing_strength: "weak" | "medium" | "strong" | null;
}

type PolicyMode =
  | "focused_grounded"
  | "broad_partial"
  | "clarification_required"
  | "out_of_scope"
  | "route_mismatch"
  | "empty"
  | "no_grounded"
  | "backend_error";

interface PolicyDecision {
  mode: PolicyMode;
  fallback_type: AssistantFallbackType;
  reply_type: AssistantReplyType;
}

interface MissingAnchors {
  period: boolean;
  account: boolean;
  documentOrObject: boolean;
  counterparty: boolean;
  anomalyType: boolean;
}

const PROBLEM_HEAVY_TYPES = new Set<ProblemUnitType>([
  "document_conflict",
  "broken_chain_segment",
  "lifecycle_anomaly_node",
  "unresolved_settlement_cluster",
  "period_risk_cluster",
  "cross_branch_inconsistency_cluster"
]);

function flattenEvidence(results: UnifiedRetrievalResult[]): EvidenceItem[] {
  return results.flatMap((item) => item.evidence);
}

function flattenProblemUnits(results: UnifiedRetrievalResult[]): ProblemUnit[] {
  const units: ProblemUnit[] = [];
  for (const result of results) {
    if (!Array.isArray(result.problem_units)) {
      continue;
    }
    units.push(...result.problem_units);
  }
  const byId = new Map<string, ProblemUnit>();
  for (const unit of units) {
    byId.set(unit.problem_unit_id, unit);
  }
  return Array.from(byId.values());
}

function selectProblemUnitSummary(results: UnifiedRetrievalResult[]): ProblemUnitSummary | null {
  let selected: ProblemUnitSummary | null = null;
  for (const result of results) {
    if (!result.problem_unit_summary) {
      continue;
    }
    if (!selected || result.problem_unit_summary.units_total > selected.units_total) {
      selected = result.problem_unit_summary;
    }
  }
  return selected;
}

function formatAffectedScope(unit: ProblemUnit): string {
  const accountScope = sanitizeUserLines(unit.affected_accounts, 2);
  const counterpartyScope = sanitizeUserLines(unit.affected_counterparties, 2);
  const documentScope = sanitizeUserLines(unit.affected_documents, 2);
  const entityScope = sanitizeUserLines(unit.affected_entities, 2);
  const scopeParts: string[] = [];
  if (accountScope.length > 0) {
    scopeParts.push(`accounts: ${accountScope.join(", ")}`);
  }
  if (counterpartyScope.length > 0) {
    scopeParts.push(`counterparties: ${counterpartyScope.join(", ")}`);
  }
  if (documentScope.length > 0) {
    scopeParts.push(`documents: ${documentScope.join(", ")}`);
  }
  if (scopeParts.length === 0 && entityScope.length > 0) {
    scopeParts.push(`entities: ${entityScope.join(", ")}`);
  }
  if (scopeParts.length === 0) {
    return "affected scope requires clarification";
  }
  return scopeParts.join("; ");
}

function formatLifecycleScope(unit: ProblemUnit): string | null {
  if (!unit.lifecycle_domain) {
    return null;
  }
  const parts: string[] = [`domain=${unit.lifecycle_domain}`];
  if (unit.current_lifecycle_state) {
    parts.push(`current=${unit.current_lifecycle_state}`);
  }
  if (unit.expected_lifecycle_state) {
    parts.push(`expected=${unit.expected_lifecycle_state}`);
  }
  if (unit.lifecycle_defect_type) {
    parts.push(`defect=${unit.lifecycle_defect_type}`);
  }
  if (unit.missing_transition) {
    parts.push(`missing_transition=${unit.missing_transition}`);
  }
  if (unit.invalid_transition) {
    parts.push(`invalid_transition=${unit.invalid_transition}`);
  }
  if (unit.stale_duration) {
    parts.push(`stale_duration=${unit.stale_duration}`);
  }
  return parts.join(", ");
}

function rankProblemUnitsForAnswer(units: ProblemUnit[], lifecycleAnswerEnabled: boolean): ProblemUnit[] {
  if (!lifecycleAnswerEnabled) {
    return units.slice().sort((left, right) => {
      const severityDiff = right.severity.score - left.severity.score;
      if (severityDiff !== 0) return severityDiff;
      return right.confidence.score - left.confidence.score;
    });
  }
  return units.slice().sort((left, right) => {
    const lifecycleRankDiff = (right.lifecycle_ranking_score ?? 0) - (left.lifecycle_ranking_score ?? 0);
    if (lifecycleRankDiff !== 0) return lifecycleRankDiff;
    const lifecycleConfidenceDiff = (right.lifecycle_confidence?.score ?? 0) - (left.lifecycle_confidence?.score ?? 0);
    if (lifecycleConfidenceDiff !== 0) return lifecycleConfidenceDiff;
    const severityDiff = right.severity.score - left.severity.score;
    if (severityDiff !== 0) return severityDiff;
    return right.confidence.score - left.confidence.score;
  });
}

function hasLifecycleResolution(units: ProblemUnit[]): boolean {
  return units.some(
    (unit) =>
      Boolean(unit.lifecycle_domain) &&
      Boolean(unit.current_lifecycle_state) &&
      Boolean(unit.expected_lifecycle_state) &&
      Boolean(unit.lifecycle_defect_type)
  );
}

function buildProblemCentricActions(input: {
  units: ProblemUnit[];
  mode: PolicyMode;
  missingAnchors: MissingAnchors;
  coverageReport: RequirementCoverageReport;
}): string[] {
  const actions: string[] = [];
  const unitTypes = new Set(input.units.map((item) => item.problem_unit_type));

  if (unitTypes.has("broken_chain_segment")) {
    actions.push("РџСЂРѕРІРµСЂСЊС‚Рµ СЃРІСЏР·РєСѓ РІС‹РїРёСЃРєР° -> РґРѕРєСѓРјРµРЅС‚ -> РїСЂРѕРІРѕРґРєР° РїРѕ РїСЂРѕР±Р»РµРјРЅС‹Рј СѓС‡Р°СЃС‚РєР°Рј С†РµРїРѕС‡РєРё.");
  }
  if (unitTypes.has("unresolved_settlement_cluster")) {
    actions.push("РЎРІРµСЂСЊС‚Рµ С…РІРѕСЃС‚С‹ РїРѕ СЂР°СЃС‡РµС‚Р°Рј: Р·Р°РєСЂС‹Р»СЃСЏ Р»Рё РґРѕРєСѓРјРµРЅС‚ РѕРїР»Р°С‚С‹ РєРѕСЂСЂРµРєС‚РЅС‹Рј Р·Р°РєСЂС‹РІР°СЋС‰РёРј РґРѕРєСѓРјРµРЅС‚РѕРј.");
  }
  if (unitTypes.has("period_risk_cluster")) {
    actions.push("РћС†РµРЅРёС‚Рµ РІР»РёСЏРЅРёРµ РґРµС„РµРєС‚Р° РЅР° Р·Р°РєСЂС‹С‚РёРµ РїРµСЂРёРѕРґР° Рё РєРѕСЂСЂРµРєС‚РЅРѕСЃС‚СЊ СЂРµРіР»Р°РјРµРЅС‚РЅС‹С… РѕРїРµСЂР°С†РёР№.");
  }
  if (unitTypes.has("cross_branch_inconsistency_cluster")) {
    actions.push("РЎРІРµСЂСЊС‚Рµ РїСЂРѕС‚РёРІРѕСЂРµС‡РёСЏ РјРµР¶РґСѓ РґРѕРєСѓРјРµРЅС‚Р°РјРё, РїСЂРѕРІРѕРґРєР°РјРё Рё СЂРµРіРёСЃС‚СЂР°РјРё РїРѕ РќР”РЎ/РјРµР¶РєРѕРЅС‚СѓСЂРЅС‹Рј СЃРІСЏР·СЏРј.");
  }
  if (unitTypes.has("lifecycle_anomaly_node")) {
    actions.push("РџСЂРѕРІРµСЂСЊС‚Рµ lifecycle РѕР±СЉРµРєС‚Р°: РѕР¶РёРґР°РµРјС‹Р№ СЌС‚Р°Рї РЅРµ РґРѕР»Р¶РµРЅ РѕСЃС‚Р°РІР°С‚СЊСЃСЏ РІ partially_linked СЃРѕСЃС‚РѕСЏРЅРёРё.");
  }
  for (const unit of input.units) {
    if (unit.lifecycle_defect_type === "stale_active_state") {
      actions.push("РџСЂРѕРІРµСЂСЊС‚Рµ, РїРѕС‡РµРјСѓ РѕР±СЉРµРєС‚ Р·Р°РІРёСЃ: РѕР¶РёРґР°РµРјС‹Р№ РїРµСЂРµС…РѕРґ РЅРµ РґРѕР»Р¶РµРЅ РѕСЃС‚Р°РІР°С‚СЊСЃСЏ РІ Р°РєС‚РёРІРЅРѕР№ СЃС‚Р°РґРёРё.");
    }
    if (unit.lifecycle_defect_type === "misclosed_state") {
      actions.push("РџСЂРѕРІРµСЂСЊС‚Рµ Р·Р°РєСЂС‹РІР°СЋС‰РёР№ РґРѕРєСѓРјРµРЅС‚ Рё РїСЂРѕРІРѕРґРєРё: Р·Р°РєСЂС‹С‚РёРµ РјРѕР¶РµС‚ Р±С‹С‚СЊ С„РѕСЂРјР°Р»СЊРЅС‹Рј, РЅРѕ РЅРµРєРѕСЂСЂРµРєС‚РЅС‹Рј РїРѕ РїСѓС‚Рё.");
    }
    if (unit.lifecycle_defect_type === "cross_branch_state_conflict") {
      actions.push("РЎРІРµСЂСЊС‚Рµ Р±СѓС…РіР°Р»С‚РµСЂСЃРєСѓСЋ Рё СЃРјРµР¶РЅСѓСЋ РІРµС‚РєРё (РЅР°РїСЂРёРјРµСЂ, РќР”РЎ/СЂР°СЃС‡РµС‚С‹): РѕР±РЅР°СЂСѓР¶РµРЅ РјРµР¶РєРѕРЅС‚СѓСЂРЅС‹Р№ РєРѕРЅС„Р»РёРєС‚ СЃРѕСЃС‚РѕСЏРЅРёСЏ.");
    }
  }

  if (input.mode === "clarification_required") {
    if (input.missingAnchors.period) {
      actions.push("РЈС‚РѕС‡РЅРёС‚Рµ РїРµСЂРёРѕРґ РїСЂРѕРІРµСЂРєРё, С‡С‚РѕР±С‹ Р·Р°С„РёРєСЃРёСЂРѕРІР°С‚СЊ РіСЂР°РЅРёС†С‹ РїСЂРѕР±Р»РµРјРЅРѕРіРѕ РєРѕРЅС‚СѓСЂР°.");
    }
    if (input.missingAnchors.account) {
      actions.push("РЈС‚РѕС‡РЅРёС‚Рµ СЃС‡РµС‚ РёР»Рё РіСЂСѓРїРїСѓ СЃС‡РµС‚РѕРІ РґР»СЏ РїСЂРµРґРјРµС‚РЅРѕР№ Р»РѕРєР°Р»РёР·Р°С†РёРё РґРµС„РµРєС‚Р°.");
    }
    if (input.missingAnchors.documentOrObject) {
      actions.push("РЈРєР°Р¶РёС‚Рµ РєРѕРЅРєСЂРµС‚РЅС‹Р№ РґРѕРєСѓРјРµРЅС‚ РёР»Рё РѕР±СЉРµРєС‚ С‚СЂР°СЃСЃРёСЂРѕРІРєРё РґР»СЏ РїСЂРѕРІРµСЂРєРё РјРµС…Р°РЅРёР·РјР° РѕС‚РєР»РѕРЅРµРЅРёСЏ.");
    }
    if (input.missingAnchors.counterparty) {
      actions.push("РЈРєР°Р¶РёС‚Рµ РєРѕРЅС‚СЂР°РіРµРЅС‚Р°/РґРѕРіРѕРІРѕСЂ, С‡С‚РѕР±С‹ РїСЂРѕРІРµСЂРёС‚СЊ С…РІРѕСЃС‚С‹ Рё СЂР°Р·СЂС‹РІС‹ РЅР° РєРѕРЅРєСЂРµС‚РЅРѕР№ СЃРІСЏР·РєРµ.");
    }
  }

  if (input.coverageReport.requirements_uncovered.length > 0) {
    actions.push(`Р—Р°РєСЂРѕР№С‚Рµ РЅРµРїРѕРєСЂС‹С‚С‹Рµ С‚СЂРµР±РѕРІР°РЅРёСЏ: ${input.coverageReport.requirements_uncovered.join(", ")}.`);
  }

  return uniqueStrings(actions, 6);
}

function buildProblemCentricClarifications(input: {
  units: ProblemUnit[];
  missingAnchors: MissingAnchors;
  coverageReport: RequirementCoverageReport;
  mode: PolicyMode;
}): string[] {
  if (input.mode !== "clarification_required") {
    return [];
  }

  const questions: string[] = [];
  const unitTypes = new Set(input.units.map((item) => item.problem_unit_type));

  if (input.missingAnchors.period) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ РїРµСЂРёРѕРґ (РЅР°РїСЂРёРјРµСЂ, 2020-06), РІ РєРѕС‚РѕСЂРѕРј РЅСѓР¶РЅРѕ РїСЂРѕРІРµСЂРёС‚СЊ РїСЂРѕР±Р»РµРјРЅС‹Р№ РєР»Р°СЃС‚РµСЂ.");
  }
  if (input.missingAnchors.account) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ СЃС‡РµС‚ РёР»Рё СЃРІСЏР·РєСѓ СЃС‡РµС‚РѕРІ (РЅР°РїСЂРёРјРµСЂ, 51/60), РіРґРµ РІС‹ РѕР¶РёРґР°РµС‚Рµ РґРµС„РµРєС‚.");
  }
  if (input.missingAnchors.documentOrObject) {
    questions.push("РЈРєР°Р¶РёС‚Рµ РґРѕРєСѓРјРµРЅС‚/РѕР±СЉРµРєС‚, РѕС‚ РєРѕС‚РѕСЂРѕРіРѕ РЅСѓР¶РЅРѕ СЃС‚СЂРѕРёС‚СЊ РїСЂРѕРІРµСЂРєСѓ С†РµРїРѕС‡РєРё.");
  }
  if (input.missingAnchors.counterparty) {
    questions.push("РЈРєР°Р¶РёС‚Рµ РєРѕРЅС‚СЂР°РіРµРЅС‚Р° РёР»Рё РґРѕРіРѕРІРѕСЂ, РїРѕ РєРѕС‚РѕСЂРѕРјСѓ РїСЂРѕРІРµСЂРёС‚СЊ РЅРµР·Р°РєСЂС‹С‚СѓСЋ СЌРєСЃРїРѕР·РёС†РёСЋ.");
  }
  if (unitTypes.has("broken_chain_segment")) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ СѓС‡Р°СЃС‚РѕРє С†РµРїРѕС‡РєРё: РІС‹РїРёСЃРєР°, РїР»Р°С‚РµР¶РЅС‹Р№ РґРѕРєСѓРјРµРЅС‚ РёР»Рё РїСЂРѕРІРѕРґРєР°.");
  }
  if (unitTypes.has("period_risk_cluster")) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ, РєР°РєРѕР№ СЌС‚Р°Рї Р·Р°РєСЂС‹С‚РёСЏ РїРµСЂРёРѕРґР° РєСЂРёС‚РёС‡РµРЅ: РЅР°С‡РёСЃР»РµРЅРёРµ, Р·Р°РєСЂС‹С‚РёРµ СЃС‡РµС‚РѕРІ РёР»Рё РќР”РЎ-Р±Р»РѕРє.");
  }
  if (unitTypes.has("unresolved_settlement_cluster")) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ, РёРЅС‚РµСЂРµСЃСѓСЋС‚ С…РІРѕСЃС‚С‹ РїРѕСЃС‚Р°РІС‰РёРєРѕРІ, РїРѕРєСѓРїР°С‚РµР»РµР№ РёР»Рё РѕР±Р° РЅР°РїСЂР°РІР»РµРЅРёСЏ.");
  }
  if (input.coverageReport.clarification_needed_for.length > 0) {
    questions.push(`Р—Р°РєСЂРѕР№С‚Рµ СѓС‚РѕС‡РЅРµРЅРёСЏ РґР»СЏ С‚СЂРµР±РѕРІР°РЅРёР№: ${input.coverageReport.clarification_needed_for.join(", ")}.`);
  }

  return uniqueStrings(questions, 6);
}

function buildClaimEvidenceLinks(results: UnifiedRetrievalResult[]): NonNullable<AnswerStructureV11["evidence_block"]["claim_evidence_links"]> {
  const byClaim = new Map<string, string[]>();
  for (const evidence of flattenEvidence(results)) {
    const claimRef = String(evidence.claim_ref ?? "").trim();
    const evidenceId = String(evidence.evidence_id ?? "").trim();
    if (!claimRef || !evidenceId) {
      continue;
    }
    const current = byClaim.get(claimRef) ?? [];
    current.push(evidenceId);
    byClaim.set(claimRef, current);
  }
  return Array.from(byClaim.entries())
    .slice(0, 10)
    .map(([claim_ref, evidenceIds]) => ({
      claim_ref,
      evidence_ids: uniqueStrings(evidenceIds, 10)
    }));
}

function aggregatePolicySignals(results: UnifiedRetrievalResult[]): PolicySignals {
  const broad_query_detected = results.some((item) => summaryBoolean(item, "broad_query_detected"));
  const broad_result_flag = results.some((item) => summaryBoolean(item, "broad_result_flag"));
  const minimum_evidence_failed = results.some((item) => summaryBoolean(item, "minimum_evidence_failed"));

  let degraded_to: PolicySignals["degraded_to"] = null;
  for (const result of results) {
    const degraded = summaryString(result, "degraded_to");
    if (degraded === "clarification") {
      degraded_to = "clarification";
      break;
    }
    if (degraded === "partial") {
      degraded_to = "partial";
    }
  }

  const narrowingOrder: Record<"weak" | "medium" | "strong", number> = {
    weak: 0,
    medium: 1,
    strong: 2
  };
  let narrowing_strength: PolicySignals["narrowing_strength"] = null;
  for (const result of results) {
    const value = summaryString(result, "narrowing_strength");
    if (value !== "weak" && value !== "medium" && value !== "strong") {
      continue;
    }
    if (!narrowing_strength || narrowingOrder[value] < narrowingOrder[narrowing_strength]) {
      narrowing_strength = value;
    }
  }

  return {
    broad_query_detected,
    broad_result_flag,
    minimum_evidence_failed,
    degraded_to,
    narrowing_strength
  };
}

function confidenceToScore(value: EvidenceConfidence): number {
  if (value === "high") return 3;
  if (value === "medium") return 2;
  return 1;
}

function aggregateConfidence(results: UnifiedRetrievalResult[], evidenceItems: EvidenceItem[]): EvidenceConfidence {
  const scores: number[] = [];
  for (const evidence of evidenceItems) {
    scores.push(confidenceToScore(evidence.confidence));
  }
  for (const result of results) {
    if (result.status === "error") {
      continue;
    }
    scores.push(confidenceToScore(result.confidence));
  }
  if (scores.length === 0) {
    return "low";
  }
  const average = scores.reduce((acc, item) => acc + item, 0) / scores.length;
  if (average >= 2.6) return "high";
  if (average >= 1.8) return "medium";
  return "low";
}

function collectLimitationReasonCodes(evidenceItems: EvidenceItem[]): EvidenceLimitationReasonCode[] {
  const codes = evidenceItems
    .map((item) => item.limitation?.reason_code ?? null)
    .filter((item): item is EvidenceLimitationReasonCode => Boolean(item));
  return uniqueStrings(codes, 8) as EvidenceLimitationReasonCode[];
}

function limitationReasonToText(code: EvidenceLimitationReasonCode): string {
  if (code === "snapshot_only") return "Evidence is snapshot-only and may lag source-of-record.";
  if (code === "heuristic_inference") return "Part of the conclusion relies on heuristic inference.";
  if (code === "missing_mechanism") return "Mechanism is unresolved for part of the evidence.";
  if (code === "weak_source_mapping") return "Source mapping is weak for part of the evidence.";
  if (code === "insufficient_detail") return "Evidence lacks detail for a strong factual claim.";
  return "Some evidence limitations remain unresolved.";
}

function detectMissingAnchors(userMessage: string): MissingAnchors {
  const lower = String(userMessage ?? "").toLowerCase();
  const hasPeriod = /\b20\d{2}(?:[-./](?:0[1-9]|1[0-2]))?\b/.test(lower);
  const hasAccount = /(?:\bСЃС‡РµС‚\b|\baccount\b|\bschet\b|\b\d{2}(?:\.\d{2})?\b)/i.test(lower);
  const hasDocumentOrObject = /(?:РґРѕРєСѓРјРµРЅС‚|invoice|guid|object|obj|#\d+|\bid\b|\bref\b|dokument|doc)/i.test(lower);
  const hasCounterparty = /(?:РєРѕРЅС‚СЂР°РіРµРЅС‚|supplier|buyer|customer|kontragent|postavsh|pokupatel)/i.test(lower);
  const hasAnomalyType = /(?:Р°РЅРѕРјР°Р»|risk|РѕС‚РєР»РѕРЅ|СЂР°Р·СЂС‹РІ|mismatch|duplicate|tail|С†РµРїРѕС‡Рє|anomali|hvost)/i.test(lower);

  return {
    period: !hasPeriod,
    account: !hasAccount,
    documentOrObject: !hasDocumentOrObject,
    counterparty: !hasCounterparty,
    anomalyType: !hasAnomalyType
  };
}

function buildClarificationQuestions(input: {
  mode: PolicyMode;
  missingAnchors: MissingAnchors;
  coverageReport: RequirementCoverageReport;
  policySignals: PolicySignals;
}): string[] {
  const questions: string[] = [];
  const shouldAsk = input.mode === "clarification_required" || input.coverageReport.clarification_needed_for.length > 0;
  if (!shouldAsk) {
    return questions;
  }

  if (input.missingAnchors.period) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ РїРµСЂРёРѕРґ РїСЂРѕРІРµСЂРєРё (РЅР°РїСЂРёРјРµСЂ, 2020-06).");
  }
  if (input.missingAnchors.account) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ СЃС‡РµС‚ РёР»Рё РіСЂСѓРїРїСѓ СЃС‡РµС‚РѕРІ (РЅР°РїСЂРёРјРµСЂ, 19, 60, 62).");
  }
  if (input.missingAnchors.documentOrObject) {
    questions.push("РЈРєР°Р¶РёС‚Рµ РґРѕРєСѓРјРµРЅС‚/GUID/РєРѕРЅРєСЂРµС‚РЅС‹Р№ РѕР±СЉРµРєС‚ РґР»СЏ С‚СЂР°СЃСЃРёСЂРѕРІРєРё.");
  }
  if (input.missingAnchors.counterparty) {
    questions.push("РЈРєР°Р¶РёС‚Рµ РєРѕРЅС‚СЂР°РіРµРЅС‚Р° РёР»Рё РіСЂСѓРїРїСѓ РєРѕРЅС‚СЂР°РіРµРЅС‚РѕРІ.");
  }
  if (input.policySignals.broad_query_detected && input.missingAnchors.anomalyType) {
    questions.push("РЈС‚РѕС‡РЅРёС‚Рµ С‚РёРї РѕС‚РєР»РѕРЅРµРЅРёСЏ: СЂР°Р·СЂС‹РІ С†РµРїРѕС‡РєРё, РЅРµРІРµСЂРЅС‹Р№ РґРѕРєСѓРјРµРЅС‚ РёР»Рё Р°РЅРѕРјР°Р»СЊРЅС‹Р№ СЂРёСЃРє.");
  }
  if (input.coverageReport.clarification_needed_for.length > 0) {
    questions.push(`Р—Р°РєСЂРѕР№С‚Рµ СѓС‚РѕС‡РЅРµРЅРёСЏ РґР»СЏ С‚СЂРµР±РѕРІР°РЅРёР№: ${input.coverageReport.clarification_needed_for.join(", ")}.`);
  }

  return uniqueStrings(questions, 6);
}

function buildRecommendedActions(input: {
  mode: PolicyMode;
  coverageReport: RequirementCoverageReport;
  policySignals: PolicySignals;
  limitationReasonCodes: EvidenceLimitationReasonCode[];
  sourceRefs: string[];
}): string[] {
  const actions: string[] = [];
  if (input.mode === "focused_grounded") {
    actions.push("РџСЂРѕРІРµСЂСЊС‚Рµ 1-2 РєР»СЋС‡РµРІС‹Рµ Р·Р°РїРёСЃРё РІ СѓС‡РµС‚РЅРѕР№ Р±Р°Р·Рµ Рё Р·Р°С„РёРєСЃРёСЂСѓР№С‚Рµ РёС‚РѕРі РІ СЂР°Р±РѕС‡РµРј С„Р°Р№Р»Рµ РїСЂРѕРІРµСЂРєРё.");
  }
  if (input.mode === "broad_partial") {
    actions.push("РЎСѓР·СЊС‚Рµ Р·Р°РїСЂРѕСЃ РґРѕ РїРµСЂРёРѕРґР° + СЃС‡РµС‚Р° РёР»Рё РїРµСЂРёРѕРґР° + РґРѕРєСѓРјРµРЅС‚Р° Рё РїРѕРІС‚РѕСЂРёС‚Рµ РїСЂРѕРІРµСЂРєСѓ.");
  }
  if (input.mode === "clarification_required") {
    actions.push("Р”Р°Р№С‚Рµ РЅРµРґРѕСЃС‚Р°СЋС‰РёРµ СЏРєРѕСЂСЏ (РїРµСЂРёРѕРґ/СЃС‡РµС‚/РѕР±СЉРµРєС‚), РёРЅР°С‡Рµ СЃРёР»СЊРЅС‹Р№ factual РІС‹РІРѕРґ РЅРµРІРѕР·РјРѕР¶РµРЅ.");
  }
  if (input.coverageReport.requirements_uncovered.length > 0) {
    actions.push(`Р—Р°РєСЂРѕР№С‚Рµ РЅРµРїРѕРєСЂС‹С‚С‹Рµ С‚СЂРµР±РѕРІР°РЅРёСЏ: ${input.coverageReport.requirements_uncovered.join(", ")}.`);
  }
  if (input.coverageReport.requirements_partially_covered.length > 0) {
    actions.push(`Р”РѕСѓС‚РѕС‡РЅРёС‚Рµ С‡Р°СЃС‚РёС‡РЅРѕ РїРѕРєСЂС‹С‚С‹Рµ С‚СЂРµР±РѕРІР°РЅРёСЏ: ${input.coverageReport.requirements_partially_covered.join(", ")}.`);
  }
  if (input.policySignals.broad_query_detected && input.policySignals.narrowing_strength !== "strong") {
    actions.push("Р”РѕР±Р°РІСЊС‚Рµ Р±РѕР»РµРµ СѓР·РєРёР№ РєРѕРЅС‚РµРєСЃС‚: С‚РёРї РѕС‚РєР»РѕРЅРµРЅРёСЏ, РіСЂСѓРїРїСѓ РґРѕРєСѓРјРµРЅС‚РѕРІ Рё Р±РёР·РЅРµСЃ-СѓС‡Р°СЃС‚РѕРє.");
  }
  if (input.limitationReasonCodes.includes("snapshot_only")) {
    actions.push("РЎРІРµСЂСЊС‚Рµ РєСЂРёС‚РёС‡РЅС‹Рµ РІС‹РІРѕРґС‹ СЃ live source-of-record РІ 1C.");
  }
  if (input.limitationReasonCodes.includes("weak_source_mapping")) {
    actions.push("РџСЂРѕРІРµСЂСЊС‚Рµ source mapping РґР»СЏ СЃРІСЏР·РµР№ document/register РїРѕ СѓРєР°Р·Р°РЅРЅС‹Рј ref.");
  }
  if (input.sourceRefs.length > 0) {
    actions.push(`РќР°С‡РЅРёС‚Рµ РїСЂРѕРІРµСЂРєСѓ СЃ ${input.sourceRefs.length} РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅС‹С… Р·Р°РїРёСЃРµР№ Рё СЃРІРµСЂСЊС‚Рµ РёС… СЃ РїРµСЂРІРёС‡РЅС‹РјРё РґРѕРєСѓРјРµРЅС‚Р°РјРё.`);
  }

  return uniqueStrings(actions, 6);
}

function firstMeaningfulFact(results: UnifiedRetrievalResult[]): string | null {
  const facts = extractTopFacts(results);
  return facts.length > 0 ? facts[0] : null;
}

function buildPolicyDecision(input: {
  fallbackType: AssistantFallbackType;
  coverageReport: RequirementCoverageReport;
  groundingCheck: AnswerGroundingCheck;
  okResults: UnifiedRetrievalResult[];
  partialResults: UnifiedRetrievalResult[];
  emptyResults: UnifiedRetrievalResult[];
  errorResults: UnifiedRetrievalResult[];
  hasSupport: boolean;
  focusedStrong: boolean;
  policySignals: PolicySignals;
}): PolicyDecision {
  const hasCoverageGaps =
    input.coverageReport.requirements_uncovered.length > 0 ||
    input.coverageReport.requirements_partially_covered.length > 0 ||
    input.coverageReport.clarification_needed_for.length > 0 ||
    input.coverageReport.out_of_scope_requirements.length > 0;

  if (input.fallbackType === "out_of_scope" && input.coverageReport.requirements_covered === 0) {
    return {
      mode: "out_of_scope",
      fallback_type: "out_of_scope",
      reply_type: "out_of_scope"
    };
  }

  if (input.groundingCheck.status === "route_mismatch_blocked") {
    return {
      mode: "route_mismatch",
      fallback_type: "partial",
      reply_type: "route_mismatch_blocked"
    };
  }

  if (
    (input.policySignals.degraded_to === "clarification" && input.policySignals.minimum_evidence_failed) ||
    (input.fallbackType === "clarification" && !input.hasSupport) ||
    (input.groundingCheck.status === "no_grounded_answer" && !input.hasSupport)
  ) {
    return {
      mode: "clarification_required",
      fallback_type: "clarification",
      reply_type: "clarification_required"
    };
  }

  if (input.errorResults.length > 0 && input.okResults.length === 0 && input.partialResults.length === 0) {
    return {
      mode: "backend_error",
      fallback_type: input.fallbackType,
      reply_type: "backend_error"
    };
  }

  if (input.okResults.length === 0 && input.partialResults.length === 0 && input.emptyResults.length > 0) {
    return {
      mode: "empty",
      fallback_type: input.fallbackType,
      reply_type: "empty_but_valid"
    };
  }

  if (input.groundingCheck.status === "no_grounded_answer" && input.okResults.length === 0 && input.partialResults.length === 0) {
    return {
      mode: "no_grounded",
      fallback_type: input.fallbackType,
      reply_type: "no_grounded_answer"
    };
  }

  if (
    input.focusedStrong &&
    !input.policySignals.broad_query_detected &&
    !input.policySignals.minimum_evidence_failed &&
    !hasCoverageGaps
  ) {
    return {
      mode: "focused_grounded",
      fallback_type: "none",
      reply_type: "factual_with_explanation"
    };
  }

  if (
    input.okResults.length > 0 ||
    input.partialResults.length > 0 ||
    hasCoverageGaps ||
    input.policySignals.minimum_evidence_failed ||
    input.policySignals.broad_result_flag ||
    input.groundingCheck.status === "partial"
  ) {
    return {
      mode: "broad_partial",
      fallback_type: "partial",
      reply_type: "partial_coverage"
    };
  }

  return {
    mode: "backend_error",
    fallback_type: "unknown",
    reply_type: "backend_error"
  };
}

function buildAnswerSummary(mode: PolicyMode): string {
  if (mode === "focused_grounded") return "РЎС„РѕСЂРјРёСЂРѕРІР°РЅ РїСЂСЏРјРѕР№ РѕС‚РІРµС‚ РЅР° РѕСЃРЅРѕРІРµ РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅРѕР№ РѕРїРѕСЂС‹.";
  if (mode === "broad_partial") return "Р’С‹РІРѕРґ РѕРіСЂР°РЅРёС‡РµРЅ: РµСЃС‚СЊ С‡Р°СЃС‚РёС‡РЅР°СЏ РѕРїРѕСЂР°, РЅРѕ РЅРµ РїРѕР»РЅС‹Р№ coverage.";
  if (mode === "clarification_required") return "РќСѓР¶РЅС‹ СѓС‚РѕС‡РЅРµРЅРёСЏ: Р±РµР· СЃСѓР¶РµРЅРёСЏ strong factual РІС‹РІРѕРґ РЅРµРЅР°РґРµР¶РµРЅ.";
  if (mode === "out_of_scope") return "Р—Р°РїСЂРѕСЃ РІРЅРµ РґРѕСЃС‚СѓРїРЅРѕРіРѕ СѓС‡РµС‚РЅРѕРіРѕ РєРѕРЅС‚СѓСЂР°.";
  if (mode === "route_mismatch") return "Р РµР·СѓР»СЊС‚Р°С‚ РјР°СЂС€СЂСѓС‚Р° РЅРµ СЃРѕРІРїР°Р» СЃ РїСЂРµРґРјРµС‚РѕРј РІРѕРїСЂРѕСЃР°.";
  if (mode === "empty") return "Р’ С‚РµРєСѓС‰РµРј СЃСЂРµР·Рµ РґР°РЅРЅС‹С… СЂРµР»РµРІР°РЅС‚РЅС‹Рµ Р·Р°РїРёСЃРё РЅРµ РѕР±РЅР°СЂСѓР¶РµРЅС‹.";
  if (mode === "no_grounded") return "РќРµРґРѕСЃС‚Р°С‚РѕС‡РЅРѕ РѕРїРѕСЂС‹ РґР»СЏ РѕР±РѕСЃРЅРѕРІР°РЅРЅРѕРіРѕ РѕС‚РІРµС‚Р°.";
  return "РќРµ СѓРґР°Р»РѕСЃСЊ СЃРѕР±СЂР°С‚СЊ РѕР±РѕСЃРЅРѕРІР°РЅРЅС‹Р№ РѕС‚РІРµС‚ РїРѕ С‚РµРєСѓС‰РµРјСѓ Р·Р°РїСЂРѕСЃСѓ.";
}

function buildDirectAnswer(input: {
  mode: PolicyMode;
  retrievalResults: UnifiedRetrievalResult[];
  policySignals: PolicySignals;
}): string {
  const topFact = firstMeaningfulFact(input.retrievalResults);
  if (input.mode === "focused_grounded") {
    return topFact ?? "РџРѕРґС‚РІРµСЂР¶РґРµРЅРЅС‹Р№ СЂРµР·СѓР»СЊС‚Р°С‚ РїРѕР»СѓС‡РµРЅ; РјРѕР¶РЅРѕ РїСЂРѕРґРѕР»Р¶Р°С‚СЊ РїСЂРµРґРјРµС‚РЅСѓСЋ РїСЂРѕРІРµСЂРєСѓ Р±РµР· РґРµРіСЂР°РґР°С†РёРё.";
  }
  if (input.mode === "broad_partial") {
    if (topFact) {
      return `Р”РѕСЃС‚СѓРїРµРЅ РѕРіСЂР°РЅРёС‡РµРЅРЅС‹Р№ РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅС‹Р№ С„СЂР°РіРјРµРЅС‚: ${topFact}`;
    }
    return "Р•СЃС‚СЊ С‚РѕР»СЊРєРѕ РѕРіСЂР°РЅРёС‡РµРЅРЅР°СЏ РѕРїРѕСЂР°; РІС‹РІРѕРґ РґР°РЅ РІ С‡Р°СЃС‚РёС‡РЅРѕРј СЂРµР¶РёРјРµ Р±РµР· Р»РѕР¶РЅРѕР№ С‚РѕС‡РЅРѕСЃС‚Рё.";
  }
  if (input.mode === "clarification_required") {
    return "РўРµРєСѓС‰РёР№ Р·Р°РїСЂРѕСЃ СЃР»РёС€РєРѕРј С€РёСЂРѕРєРёР№ РёР»Рё РЅРµРґРѕРѕРїСЂРµРґРµР»РµРЅ; РЅР°РґРµР¶РЅС‹Р№ factual РІС‹РІРѕРґ РїРѕРєР° РЅРµРІРѕР·РјРѕР¶РµРЅ.";
  }
  if (input.mode === "out_of_scope") {
    return "РњРѕРіСѓ РѕС‚РІРµС‡Р°С‚СЊ С‚РѕР»СЊРєРѕ РІ РїСЂРµРґРµР»Р°С… РґР°РЅРЅС‹С… РґРѕСЃС‚СѓРїРЅРѕРіРѕ СѓС‡РµС‚РЅРѕРіРѕ РєРѕРЅС‚СѓСЂР°.";
  }
  if (input.mode === "route_mismatch") {
    return "РџСЂРµРґРјРµС‚ СЂРµР·СѓР»СЊС‚Р°С‚Р° РЅРµ СЃРѕРІРїР°Р» СЃ РїСЂРµРґРјРµС‚РѕРј РІРѕРїСЂРѕСЃР°; С‚СЂРµР±СѓРµС‚СЃСЏ СѓС‚РѕС‡РЅРµРЅРёРµ С„РѕРєСѓСЃР°.";
  }
  if (input.mode === "empty") {
    return "Р’ С‚РµРєСѓС‰РµРј СЃСЂРµР·Рµ РґР°РЅРЅС‹С… РїСЂРѕР±Р»РµРјРЅС‹Рµ Р·Р°РїРёСЃРё РїРѕ Р·Р°РґР°РЅРЅРѕРјСѓ СѓСЃР»РѕРІРёСЋ РЅРµ РЅР°Р№РґРµРЅС‹.";
  }
  if (input.mode === "no_grounded") {
    return "РќРµРґРѕСЃС‚Р°С‚РѕС‡РЅРѕ РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅРѕР№ РѕРїРѕСЂС‹ РґР»СЏ РѕС‚РІРµС‚Р° РІ С‚СЂРµР±СѓРµРјРѕР№ С‚РѕС‡РЅРѕСЃС‚Рё.";
  }
  if (input.policySignals.minimum_evidence_failed) {
    return "РњР°СЂС€СЂСѓС‚ РѕС‚СЂР°Р±РѕС‚Р°Р», РЅРѕ РјРёРЅРёРјР°Р»СЊРЅР°СЏ evidence-РѕРїРѕСЂР° РЅРµ РїСЂРѕР№РґРµРЅР°.";
  }
  return "РќРµ СѓРґР°Р»РѕСЃСЊ СЃС„РѕСЂРјРёСЂРѕРІР°С‚СЊ РѕР±РѕСЃРЅРѕРІР°РЅРЅС‹Р№ РѕС‚РІРµС‚; РЅСѓР¶РЅРѕ СѓС‚РѕС‡РЅРµРЅРёРµ Р·Р°РїСЂРѕСЃР°.";
}

function buildProblemCentricAnswerSummary(input: {
  mode: PolicyMode;
  weakUnits: boolean;
  summary: ProblemUnitSummary | null;
  lifecycleEnriched: boolean;
}): string {
  if (input.lifecycleEnriched && input.summary?.lifecycle_enriched_units && input.summary.lifecycle_enriched_units > 0) {
    if (input.mode === "clarification_required") {
      return "Р’С‹СЏРІР»РµРЅС‹ lifecycle-РґРµС„РµРєС‚С‹, РЅРѕ РґР»СЏ РЅР°РґРµР¶РЅРѕРіРѕ РІС‹РІРѕРґР° С‚СЂРµР±СѓРµС‚СЃСЏ СѓС‚РѕС‡РЅРµРЅРёРµ РїСЂРµРґРјРµС‚РЅС‹С… СЏРєРѕСЂРµР№.";
    }
    return `РЎС„РѕСЂРјРёСЂРѕРІР°РЅ lifecycle-aware problem СЃСЂРµР·: РІС‹РґРµР»РµРЅРѕ ${input.summary.lifecycle_enriched_units} lifecycle-СѓР·Р»РѕРІ СЃ РїСЂРёРѕСЂРёС‚РµС‚РѕРј РїРѕ РґРµС„РµРєС‚Р°Рј РїРµСЂРµС…РѕРґР°.`;
  }
  if (input.mode === "clarification_required") {
    return "Р’С‹СЏРІР»РµРЅС‹ РїСЂРѕР±Р»РµРјРЅС‹Рµ РєР»Р°СЃС‚РµСЂС‹, РЅРѕ РґР»СЏ РЅР°РґРµР¶РЅРѕРіРѕ РІС‹РІРѕРґР° С‚СЂРµР±СѓРµС‚СЃСЏ РїСЂРµРґРјРµС‚РЅРѕРµ СѓС‚РѕС‡РЅРµРЅРёРµ С„РѕРєСѓСЃР°.";
  }
  if (input.weakUnits) {
    return "РЎС„РѕСЂРјРёСЂРѕРІР°РЅ problem-centric СЃСЂРµР· СЃ РѕРіСЂР°РЅРёС‡РµРЅРЅРѕР№ РѕРїРѕСЂРѕР№; РІС‹РІРѕРґ РїСЂРµРґРІР°СЂРёС‚РµР»СЊРЅС‹Р№ Рё С‚СЂРµР±СѓРµС‚ РґРѕ-РїСЂРѕРІРµСЂРєРё.";
  }
  if (input.summary?.units_total && input.summary.units_total > 1) {
    return `РЎС„РѕСЂРјРёСЂРѕРІР°РЅ problem-centric СЃСЂРµР·: РІС‹РґРµР»РµРЅРѕ ${input.summary.units_total} РїСЂРѕР±Р»РµРјРЅС‹С… РєР»Р°СЃС‚РµСЂР° СЃ РїСЂРёРѕСЂРёС‚РµС‚Р°РјРё.`;
  }
  return "РЎС„РѕСЂРјРёСЂРѕРІР°РЅ problem-centric СЃСЂРµР·: РІС‹РґРµР»РµРЅ РєР»СЋС‡РµРІРѕР№ РїСЂРѕР±Р»РµРјРЅС‹Р№ РєР»Р°СЃС‚РµСЂ Рё Р·Р°С‚СЂРѕРЅСѓС‚С‹Р№ РєРѕРЅС‚СѓСЂ.";
}

function buildProblemCentricDirectAnswer(input: {
  mode: PolicyMode;
  units: ProblemUnit[];
  weakUnits: boolean;
  lifecycleAnswerEnabled: boolean;
}): string {
  const lead =
    input.mode === "clarification_required"
      ? "РћР±РЅР°СЂСѓР¶РµРЅС‹ РїСЂРѕР±Р»РµРјРЅС‹Рµ Р·РѕРЅС‹, РЅРѕ Р±РµР· СѓС‚РѕС‡РЅРµРЅРёСЏ СЏРєРѕСЂРµР№ СЃРёР»СЊРЅС‹Р№ factual-РІС‹РІРѕРґ РїСЂРµР¶РґРµРІСЂРµРјРµРЅРµРЅ."
      : input.weakUnits
        ? "Р’С‹РґРµР»РµРЅС‹ РїСЂРѕР±Р»РµРјРЅС‹Рµ Р·РѕРЅС‹ СЃ РѕРіСЂР°РЅРёС‡РµРЅРЅРѕР№ РЅР°РґРµР¶РЅРѕСЃС‚СЊСЋ; РІС‹РІРѕРґ РґР°РЅ РІ РѕРіСЂР°РЅРёС‡РµРЅРЅРѕРј СЂРµР¶РёРјРµ."
        : input.lifecycleAnswerEnabled && hasLifecycleResolution(input.units)
          ? "Р’С‹РґРµР»РµРЅС‹ lifecycle-РїСЂРѕР±Р»РµРјС‹: РѕРїСЂРµРґРµР»РµРЅС‹ С‚РµРєСѓС‰РёРµ/РѕР¶РёРґР°РµРјС‹Рµ СЃС‚Р°РґРёРё Рё С‚РёРї РЅР°СЂСѓС€РµРЅРёСЏ РїРµСЂРµС…РѕРґР°."
          : "Р’С‹РґРµР»РµРЅС‹ РєР»СЋС‡РµРІС‹Рµ РїСЂРѕР±Р»РµРјРЅС‹Рµ Р·РѕРЅС‹ Рё РёС… РІР»РёСЏРЅРёРµ РЅР° СѓС‡РµС‚РЅС‹Р№ РєРѕРЅС‚СѓСЂ.";

  const unitLines = input.units.map((unit) => {
    const scope = formatAffectedScope(unit);
    const lifecycleScope = input.lifecycleAnswerEnabled ? formatLifecycleScope(unit) : null;
    const lifecycleInterpretation =
      input.lifecycleAnswerEnabled && unit.business_lifecycle_interpretation
        ? sanitizeUserText(unit.business_lifecycle_interpretation)
        : null;
    const title = sanitizeUserText(unit.title) ?? "Problem cluster detected";
    const defect = sanitizeUserText(unit.business_defect_class) ?? "detected_issue";
    const segments = [
      `${title}: ${defect}`,
      scope,
      lifecycleScope,
      lifecycleInterpretation,
      `severity=${unit.severity.grade}`,
      `confidence=${unit.confidence.grade}`,
      unit.lifecycle_confidence ? `lifecycle_confidence=${unit.lifecycle_confidence.grade}` : null
    ].filter((item): item is string => Boolean(item));
    return `- ${segments.join("; ")}.`;
  });

  if (unitLines.length === 0) {
    return `${lead}\nРџСЂРѕР±Р»РµРјРЅС‹Рµ РєР»Р°СЃС‚РµСЂС‹ РЅРµ СѓРґР°Р»РѕСЃСЊ РґРµС‚Р°Р»РёР·РёСЂРѕРІР°С‚СЊ РІ С‚РµРєСѓС‰РµРј СЃСЂРµР·Рµ.`;
  }

  return [lead, "РџСЂРѕР±Р»РµРјРЅС‹Рµ РєР»Р°СЃС‚РµСЂС‹:", ...unitLines].join("\n");
}

function buildProblemCentricAnswerStructure(input: {
  mode: PolicyMode;
  selectedUnits: ProblemUnit[];
  problemSummary: ProblemUnitSummary | null;
  evidenceItems: EvidenceItem[];
  claimEvidenceLinks: NonNullable<AnswerStructureV11["evidence_block"]["claim_evidence_links"]>;
  limitationReasonCodes: EvidenceLimitationReasonCode[];
  groundingCheck: AnswerGroundingCheck;
  retrievalResults: UnifiedRetrievalResult[];
  missingAnchors: MissingAnchors;
  coverageReport: RequirementCoverageReport;
  lifecycleAnswerEnabled: boolean;
}): AnswerStructureV11 {
  const weakUnits = input.selectedUnits.every((item) => item.confidence.grade === "low");
  const lifecycleEnriched = input.lifecycleAnswerEnabled && hasLifecycleResolution(input.selectedUnits);
  const unitMechanismNotes = uniqueStrings(
    input.selectedUnits
      .map((item) => item.mechanism_summary)
      .filter((item) => typeof item === "string" && item.trim().length > 0),
    6
  );
  const sourceRefs = uniqueStrings(
    input.evidenceItems
      .map((item) => item.source_ref?.canonical_ref)
      .filter((item): item is string => typeof item === "string" && item.trim().length > 0),
    6
  );
  const evidenceIds = uniqueStrings(input.evidenceItems.map((item) => item.evidence_id), 10);

  const mechanismStatus: AnswerStructureV11["mechanism_block"]["status"] =
    unitMechanismNotes.length === 0
      ? "unresolved"
      : weakUnits || input.limitationReasonCodes.includes("missing_mechanism")
        ? "limited"
        : "grounded";

  const problemSpecificLimitations: string[] = [];
  if (weakUnits) {
    problemSpecificLimitations.push("Problem units remain weak-confidence; conclusions are intentionally limited.");
  }
  if (input.problemSummary?.duplicate_collapses && input.problemSummary.duplicate_collapses > 0) {
    problemSpecificLimitations.push("Part of the problem signal was merged due to duplicate collapse.");
  }

  const limitations = uniqueStrings(
    [
      ...problemSpecificLimitations,
      ...input.limitationReasonCodes.map((code) => limitationReasonToText(code)),
      ...extractLimitations(input.retrievalResults),
      ...input.groundingCheck.reasons
    ],
    10
  );

  const openUncertainties = uniqueStrings(
    [
      ...input.groundingCheck.missing_requirements,
      ...(input.mode === "clarification_required" && input.missingAnchors.period ? ["missing_anchor:period"] : []),
      ...(input.mode === "clarification_required" && input.missingAnchors.account ? ["missing_anchor:account"] : []),
      ...(input.mode === "clarification_required" && input.missingAnchors.documentOrObject
        ? ["missing_anchor:document_or_object"]
        : []),
      ...(input.mode === "clarification_required" && input.missingAnchors.counterparty ? ["missing_anchor:counterparty"] : [])
    ],
    8
  );

  return {
    schema_version: "answer_structure_v1_1",
    answer_summary: buildProblemCentricAnswerSummary({
      mode: input.mode,
      weakUnits,
      summary: input.problemSummary,
      lifecycleEnriched
    }),
    direct_answer: buildProblemCentricDirectAnswer({
      mode: input.mode,
      units: input.selectedUnits,
      weakUnits,
      lifecycleAnswerEnabled: input.lifecycleAnswerEnabled
    }),
    mechanism_block: {
      status: mechanismStatus,
      mechanism_notes: unitMechanismNotes,
      limitation_reason_codes: input.limitationReasonCodes
    },
    evidence_block: {
      evidence_ids: evidenceIds,
      source_refs: sourceRefs,
      mechanism_notes: unitMechanismNotes,
      coverage_note:
        input.coverageReport.requirements_total > 0 &&
        input.coverageReport.requirements_total === input.coverageReport.requirements_covered &&
        input.coverageReport.requirements_uncovered.length === 0 &&
        input.coverageReport.requirements_partially_covered.length === 0
          ? "coverage_full_or_near_full"
          : "coverage_partial_or_limited",
      ...(input.claimEvidenceLinks.length > 0
        ? {
            claim_evidence_links: input.claimEvidenceLinks
          }
        : {})
    },
    uncertainty_block: {
      open_uncertainties: openUncertainties,
      limitations
    },
    next_step_block: {
      recommended_actions: buildProblemCentricActions({
        units: input.selectedUnits,
        mode: input.mode,
        missingAnchors: input.missingAnchors,
        coverageReport: input.coverageReport
      }),
      clarification_questions: buildProblemCentricClarifications({
        units: input.selectedUnits,
        missingAnchors: input.missingAnchors,
        coverageReport: input.coverageReport,
        mode: input.mode
      })
    }
  };
}

function renderPolicyReply(structure: AnswerStructureV11): string {
  const mechanismLines: string[] = [`status=${structure.mechanism_block.status}`];
  if (structure.mechanism_block.mechanism_notes.length > 0) {
    mechanismLines.push(...structure.mechanism_block.mechanism_notes.map((item) => `note: ${item}`));
  }
  if (structure.mechanism_block.limitation_reason_codes.length > 0) {
    mechanismLines.push(`limitation_codes: ${structure.mechanism_block.limitation_reason_codes.join(", ")}`);
  }
  if (structure.mechanism_block.status === "unresolved" && structure.mechanism_block.mechanism_notes.length === 0) {
    mechanismLines.push("mechanism_note is intentionally omitted due to weak or missing mechanism evidence");
  }

  const sourceRefCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0;
  const claimLinkCount = Array.isArray(structure.evidence_block.claim_evidence_links)
    ? structure.evidence_block.claim_evidence_links.length
    : 0;
  const evidenceLines: string[] = [
    `coverage=${structure.evidence_block.coverage_note}`,
    `supporting_evidence_count=${structure.evidence_block.evidence_ids.length}`,
    `supporting_source_count=${sourceRefCount}`,
    `claim_support_links=${claimLinkCount}`
  ];
  if (sourceRefCount > 0) {
    evidenceLines.push("Detailed source references are available in debug payload.");
  }

  const uncertaintyLines = [
    ...structure.uncertainty_block.open_uncertainties.map((item) => `open: ${item}`),
    ...structure.uncertainty_block.limitations.map((item) => `limit: ${item}`)
  ];
  if (uncertaintyLines.length === 0) {
    uncertaintyLines.push("No material uncertainty detected in current scoped answer.");
  }

  const nextStepLines = [
    ...structure.next_step_block.recommended_actions.map((item) => `action: ${item}`),
    ...structure.next_step_block.clarification_questions.map((item) => `clarify: ${item}`)
  ];
  if (nextStepLines.length === 0) {
    nextStepLines.push("No additional action is required for this scoped answer.");
  }

  return sanitizeUserFacingReply(
    [
      `Answer summary: ${structure.answer_summary}`,
      `Direct answer:\n${structure.direct_answer}`,
      `Mechanism block:\n${formatList(mechanismLines)}`,
      `Evidence block:\n${formatList(evidenceLines)}`,
      `Uncertainty block:\n${formatList(uncertaintyLines)}`,
      `Next step block:\n${formatList(nextStepLines)}`
    ]
      .filter(Boolean)
      .join("\n\n")
  );
}

function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutput {
  const fallbackType = fallbackFromSummary(input.routeSummary);
  const okResults = input.retrievalResults.filter((item) => item.status === "ok");
  const partialResults = input.retrievalResults.filter((item) => item.status === "partial");
  const emptyResults = input.retrievalResults.filter((item) => item.status === "empty");
  const errorResults = input.retrievalResults.filter((item) => item.status === "error");
  const evidenceItems = flattenEvidence(input.retrievalResults);
  const policySignals = aggregatePolicySignals(input.retrievalResults);
  const limitationReasonCodes = collectLimitationReasonCodes(evidenceItems);
  const sourceRefs = uniqueStrings(
    evidenceItems
      .map((item) => item.source_ref?.canonical_ref)
      .filter((item): item is string => typeof item === "string" && item.trim().length > 0),
    8
  );
  const mechanismNotes = uniqueStrings(
    evidenceItems
      .map((item) => item.mechanism_note)
      .filter((item): item is string => typeof item === "string" && item.trim().length > 0),
    6
  );
  const lifecycleAnswerEnabled = Boolean(input.enableLifecycleAnswerV1);
  const problemUnits = flattenProblemUnits(input.retrievalResults);
  const problemUnitSummary = selectProblemUnitSummary(input.retrievalResults);
  const problemHeavyUnits = problemUnits.filter((item) => PROBLEM_HEAVY_TYPES.has(item.problem_unit_type));
  const selectedProblemUnits = rankProblemUnitsForAnswer(problemHeavyUnits, lifecycleAnswerEnabled).slice(0, 4);
  const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults);
  const aggregateEvidenceConfidence = aggregateConfidence(input.retrievalResults, evidenceItems);
  const lowConfidenceSignals = evidenceItems.filter((item) => item.confidence === "low").length;
  const lowConfidenceShare = evidenceItems.length > 0 ? lowConfidenceSignals / evidenceItems.length : 0;
  const lowConfidenceConcentration = lowConfidenceShare >= 0.6;
  const hasSupport =
    okResults.length > 0 ||
    partialResults.length > 0 ||
    evidenceItems.length > 0 ||
    input.retrievalResults.some((item) => item.items.length > 0);
  const hasCoverageGaps =
    input.coverageReport.requirements_uncovered.length > 0 ||
    input.coverageReport.requirements_partially_covered.length > 0 ||
    input.coverageReport.clarification_needed_for.length > 0 ||
    input.coverageReport.out_of_scope_requirements.length > 0;
  const hasCriticalEvidenceLimitation =
    limitationReasonCodes.includes("weak_source_mapping") ||
    limitationReasonCodes.includes("insufficient_detail");
  const hasNonLowRouteConfidence = input.retrievalResults.some(
    (item) => item.status === "ok" && item.confidence !== "low"
  );
  const focusedStrong =
    okResults.length > 0 &&
    input.groundingCheck.status === "grounded" &&
    !hasCoverageGaps &&
    !policySignals.broad_query_detected &&
    !policySignals.broad_result_flag &&
    !policySignals.minimum_evidence_failed &&
    !hasCriticalEvidenceLimitation &&
    (aggregateEvidenceConfidence !== "low" || hasNonLowRouteConfidence);

  const decision = buildPolicyDecision({
    fallbackType,
    coverageReport: input.coverageReport,
    groundingCheck: input.groundingCheck,
    okResults,
    partialResults,
    emptyResults,
    errorResults,
    hasSupport,
    focusedStrong,
    policySignals
  });

  const missingAnchors = detectMissingAnchors(input.userMessage);
  const hasProblemWeakSignal =
    policySignals.narrowing_strength !== "strong" ||
    policySignals.minimum_evidence_failed ||
    limitationReasonCodes.includes("missing_mechanism") ||
    limitationReasonCodes.includes("weak_source_mapping") ||
    aggregateEvidenceConfidence === "low" ||
    lowConfidenceConcentration;
  const hardBlockedMode = decision.mode === "out_of_scope" || decision.mode === "route_mismatch" || decision.mode === "backend_error";
  const problemCentricModeEligible =
    decision.mode === "broad_partial" ||
    decision.mode === "clarification_required" ||
    (decision.mode === "focused_grounded" && hasProblemWeakSignal);
  const shouldUseProblemCentricAnswer =
    Boolean(input.enableProblemCentricAnswerV1) &&
    !hardBlockedMode &&
    problemCentricModeEligible &&
    (!focusedStrong || hasProblemWeakSignal) &&
    selectedProblemUnits.length > 0;

  if (shouldUseProblemCentricAnswer) {
    const problemCentricStructure = buildProblemCentricAnswerStructure({
      mode: decision.mode,
      selectedUnits: selectedProblemUnits,
      problemSummary: problemUnitSummary,
      evidenceItems,
      claimEvidenceLinks,
      limitationReasonCodes,
      groundingCheck: input.groundingCheck,
      retrievalResults: input.retrievalResults,
      missingAnchors,
      coverageReport: input.coverageReport,
      lifecycleAnswerEnabled
    });

    const lifecycleModeActive = lifecycleAnswerEnabled && hasLifecycleResolution(selectedProblemUnits);

    return {
      assistant_reply: renderPolicyReply(problemCentricStructure),
      fallback_type: decision.fallback_type,
      reply_type: decision.reply_type,
      answer_structure_v11: problemCentricStructure,
      problem_centric_answer_applied: true,
      problem_units_used_count: selectedProblemUnits.length,
      problem_answer_mode: lifecycleModeActive ? "stage3_lifecycle_aware_v1" : "stage2_problem_centric_v1",
      problem_unit_ids_used: selectedProblemUnits.map((item) => item.problem_unit_id)
    };
  }

  const clarificationQuestions = buildClarificationQuestions({
    mode: decision.mode,
    missingAnchors,
    coverageReport: input.coverageReport,
    policySignals
  });
  const recommendedActions = buildRecommendedActions({
    mode: decision.mode,
    coverageReport: input.coverageReport,
    policySignals,
    limitationReasonCodes,
    sourceRefs
  });

  const limitations = uniqueStrings(
    [
      ...limitationReasonCodes.map((code) => limitationReasonToText(code)),
      ...extractLimitations(input.retrievalResults),
      ...input.groundingCheck.reasons,
      ...(policySignals.minimum_evidence_failed ? ["Minimum evidence gate failed for current scope."] : []),
      ...(policySignals.broad_query_detected && policySignals.narrowing_strength === "weak"
        ? ["Broad query remains weakly narrowed; precision is intentionally limited."]
        : [])
    ],
    10
  );
  const openUncertainties = uniqueStrings(
    [
      ...input.groundingCheck.missing_requirements,
      ...(decision.mode === "clarification_required" && missingAnchors.period ? ["missing_anchor:period"] : []),
      ...(decision.mode === "clarification_required" && missingAnchors.account ? ["missing_anchor:account"] : []),
      ...(decision.mode === "clarification_required" && missingAnchors.documentOrObject ? ["missing_anchor:document_or_object"] : []),
      ...(decision.mode === "clarification_required" && missingAnchors.counterparty ? ["missing_anchor:counterparty"] : [])
    ],
    8
  );

  const mechanismStatus: AnswerStructureV11["mechanism_block"]["status"] =
    mechanismNotes.length === 0
      ? "unresolved"
      : limitationReasonCodes.includes("missing_mechanism") || limitationReasonCodes.includes("heuristic_inference")
        ? "limited"
        : "grounded";

  const answerStructure: AnswerStructureV11 = {
    schema_version: "answer_structure_v1_1",
    answer_summary: buildAnswerSummary(decision.mode),
    direct_answer: buildDirectAnswer({
      mode: decision.mode,
      retrievalResults: input.retrievalResults,
      policySignals
    }),
    mechanism_block: {
      status: mechanismStatus,
      mechanism_notes: mechanismNotes,
      limitation_reason_codes: limitationReasonCodes
    },
    evidence_block: {
      evidence_ids: uniqueStrings(evidenceItems.map((item) => item.evidence_id), 10),
      source_refs: sourceRefs,
      mechanism_notes: mechanismNotes,
      coverage_note:
        input.coverageReport.requirements_total > 0 &&
        input.coverageReport.requirements_total === input.coverageReport.requirements_covered &&
        input.coverageReport.requirements_uncovered.length === 0 &&
        input.coverageReport.requirements_partially_covered.length === 0
          ? "coverage_full_or_near_full"
          : "coverage_partial_or_limited",
      ...(claimEvidenceLinks.length > 0
        ? {
            claim_evidence_links: claimEvidenceLinks
          }
        : {})
    },
    uncertainty_block: {
      open_uncertainties: openUncertainties,
      limitations
    },
    next_step_block: {
      recommended_actions: recommendedActions,
      clarification_questions: clarificationQuestions
    }
  };

  return {
    assistant_reply: renderPolicyReply(answerStructure),
    fallback_type: decision.fallback_type,
    reply_type: decision.reply_type,
    answer_structure_v11: answerStructure,
    problem_centric_answer_applied: false,
    problem_units_used_count: 0,
    problem_answer_mode: "stage1_policy_v11"
  };
}

function composeExplainableAnswer(input: ComposeAnswerInput, scopeLabel: "full" | "partial"): string {
  const facts = extractTopFacts(input.retrievalResults);
  const whyIncludedRaw = extractWhyIncluded(input.retrievalResults);
  const selectionReasonsRaw = extractSelectionReasons(input.retrievalResults);
  const whyIncluded = whyIncludedRaw.length > 0 ? whyIncludedRaw : buildFallbackWhyIncluded(input.retrievalResults);
  const selectionReasons =
    selectionReasonsRaw.length > 0 ? selectionReasonsRaw : buildFallbackSelectionReasons(input.retrievalResults);
  const riskFactors = extractRiskFactors(input.retrievalResults);
  const interpretation = extractBusinessInterpretation(input.retrievalResults);
  const limitations = uniqueStrings([...extractLimitations(input.retrievalResults), ...input.groundingCheck.reasons]);
  const nextSteps = suggestNextStep(input.requirements, input.coverageReport);

  const lead =
    scopeLabel === "full"
      ? "РС‚РѕРі: Р·Р°РїСЂРѕСЃ РѕР±СЂР°Р±РѕС‚Р°РЅ РїРѕ РїСЂРµРґРјРµС‚Сѓ, РЅР°Р№РґРµРЅРЅС‹Рµ РѕР±СЉРµРєС‚С‹ РїРѕРґС‚РІРµСЂР¶РґРµРЅС‹ РґР°РЅРЅС‹РјРё РєРѕРЅС‚СѓСЂР°."
      : "РС‚РѕРі: Р·Р°РїСЂРѕСЃ РѕР±СЂР°Р±РѕС‚Р°РЅ С‡Р°СЃС‚РёС‡РЅРѕ, РЅРёР¶Рµ РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅР°СЏ С‡Р°СЃС‚СЊ Рё РѕРіСЂР°РЅРёС‡РµРЅРёСЏ.";

  return sanitizeUserFacingReply(
    [
      lead,
      facts.length > 0 ? "РџРѕРґС‚РІРµСЂР¶РґРµРЅРЅС‹Рµ СЂРµР·СѓР»СЊС‚Р°С‚С‹:\n" + formatList(facts) : "",
      whyIncluded.length > 0 ? "РџРѕС‡РµРјСѓ СЌС‚Рѕ РїРѕРїР°Р»Рѕ РІ РѕС‚РІРµС‚:\n" + formatList(whyIncluded) : "",
      selectionReasons.length > 0 ? "РћСЃРЅРѕРІР°РЅРёРµ РѕС‚Р±РѕСЂР°:\n" + formatList(selectionReasons) : "",
      riskFactors.length > 0 ? "РџРѕРґС‚РІРµСЂР¶РґР°СЋС‰РёРµ РїСЂРёР·РЅР°РєРё:\n" + formatList(riskFactors) : "",
      interpretation.length > 0 ? "РџСЂР°РєС‚РёС‡РµСЃРєРёР№ СЃРјС‹СЃР»:\n" + formatList(interpretation) : "",
      limitations.length > 0 ? "РћРіСЂР°РЅРёС‡РµРЅРёСЏ:\n" + formatList(limitations) : "",
      nextSteps.length > 0 ? "Р§С‚Рѕ РїСЂРѕРІРµСЂРёС‚СЊ РґР°Р»СЊС€Рµ:\n" + formatList(nextSteps) : ""
    ]
      .filter(Boolean)
      .join("\n\n")
  );
}

export function composeAssistantAnswer(input: ComposeAnswerInput): ComposeAnswerOutput {
  if (input.enableAnswerPolicyV11) {
    return composeAssistantAnswerV11(input);
  }

  const fallbackType = fallbackFromSummary(input.routeSummary);
  const okResults = input.retrievalResults.filter((item) => item.status === "ok");
  const partialResults = input.retrievalResults.filter((item) => item.status === "partial");
  const emptyResults = input.retrievalResults.filter((item) => item.status === "empty");
  const errorResults = input.retrievalResults.filter((item) => item.status === "error");
  const legacyEvidenceItems = flattenEvidence(input.retrievalResults);
  const legacyLimitationReasonCodes = collectLimitationReasonCodes(legacyEvidenceItems);
  const hasBroadMinimumEvidenceSignal = input.retrievalResults.some(
    (item) => summaryBoolean(item, "broad_guard_applied") && summaryBoolean(item, "minimum_evidence_failed")
  );
  const hasBroadClarificationSignal = input.retrievalResults.some(
    (item) =>
      summaryBoolean(item, "broad_guard_applied") &&
      summaryBoolean(item, "minimum_evidence_failed") &&
      summaryString(item, "degraded_to") === "clarification"
  );

  if (fallbackType === "out_of_scope" && input.coverageReport.requirements_covered === 0) {
    return {
      assistant_reply:
        "РЇ РјРѕРіСѓ РѕС‚РІРµС‡Р°С‚СЊ С‚РѕР»СЊРєРѕ РїРѕ РґР°РЅРЅС‹Рј РІР°С€РµР№ СѓС‡РµС‚РЅРѕР№ Р±Р°Р·С‹. Р­С‚РѕС‚ Р·Р°РїСЂРѕСЃ РІС‹С…РѕРґРёС‚ Р·Р° СЂР°РјРєРё РґРѕСЃС‚СѓРїРЅРѕРіРѕ РєРѕРЅС‚СѓСЂР°.",
      fallback_type: "out_of_scope",
      reply_type: "out_of_scope"
    };
  }

  if (input.groundingCheck.status === "route_mismatch_blocked") {
    return {
      assistant_reply: [
        "РќРµ РѕС‚РїСЂР°РІР»СЏСЋ С„РёРЅР°Р»СЊРЅС‹Р№ РѕС‚РІРµС‚, РїРѕС‚РѕРјСѓ С‡С‚Рѕ РїСЂРµРґРјРµС‚ СЂРµР·СѓР»СЊС‚Р°С‚Р° РЅРµ СЃРѕРІРїР°Р» СЃ РїСЂРµРґРјРµС‚РѕРј РІРѕРїСЂРѕСЃР°.",
        "РЈС‚РѕС‡РЅРёС‚Рµ С„РѕСЂРјСѓР»РёСЂРѕРІРєСѓ (РЅР°РїСЂРёРјРµСЂ, РЅСѓР¶РЅС‹Р№ СЃС‡РµС‚/СѓС‡Р°СЃС‚РѕРє СѓС‡РµС‚Р°), Рё СЏ РІС‹РїРѕР»РЅСЋ РїРѕРІС‚РѕСЂРЅС‹Р№ РїСЂРѕС…РѕРґ."
      ].join("\n\n"),
      fallback_type: "partial",
      reply_type: "route_mismatch_blocked"
    };
  }

  if (input.groundingCheck.status === "no_grounded_answer" && okResults.length === 0 && !hasBroadMinimumEvidenceSignal) {
    return {
      assistant_reply:
        "РџРѕРєР° РЅРµ СѓРґР°Р»РѕСЃСЊ СЃРѕР±СЂР°С‚СЊ РїСЂРµРґРјРµС‚РЅРѕ РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅС‹Р№ РѕС‚РІРµС‚ РїРѕ РІР°С€РµРјСѓ РІРѕРїСЂРѕСЃСѓ. РќСѓР¶РЅС‹ РґРѕРїРѕР»РЅРёС‚РµР»СЊРЅС‹Рµ СѓС‚РѕС‡РЅРµРЅРёСЏ РїРѕ РїРµСЂРёРѕРґСѓ РёР»Рё РѕР±СЉРµРєС‚Сѓ РїСЂРѕРІРµСЂРєРё.",
      fallback_type: fallbackType,
      reply_type: "no_grounded_answer"
    };
  }

  if (hasBroadClarificationSignal && okResults.length === 0 && partialResults.length === 0) {
    return {
      assistant_reply:
        "Р—Р°РїСЂРѕСЃ СЃР»РёС€РєРѕРј С€РёСЂРѕРєРёР№ РґР»СЏ РЅР°РґРµР¶РЅРѕРіРѕ РІС‹РІРѕРґР° РїРѕ С‚РµРєСѓС‰РµР№ РѕРїРѕСЂРµ. РЈС‚РѕС‡РЅРёС‚Рµ РїРµСЂРёРѕРґ, СѓС‡Р°СЃС‚РѕРє СѓС‡РµС‚Р° РёР»Рё РѕР±СЉРµРєС‚ РїСЂРѕРІРµСЂРєРё, РїРѕСЃР»Рµ С‡РµРіРѕ СЏ РґР°Рј РїСЂРµРґРјРµС‚РЅС‹Р№ СЂРµР·СѓР»СЊС‚Р°С‚.",
      fallback_type: "clarification",
      reply_type: "clarification_required"
    };
  }

  if (fallbackType === "clarification" && okResults.length === 0 && partialResults.length === 0) {
    return {
      assistant_reply: "РЈС‚РѕС‡РЅРёС‚Рµ, РїРѕР¶Р°Р»СѓР№СЃС‚Р°, РїРµСЂРёРѕРґ, СЃС‡РµС‚, РґРѕРєСѓРјРµРЅС‚ РёР»Рё РєРѕРЅС‚СЂР°РіРµРЅС‚Р°, С‡С‚РѕР±С‹ Р·Р°РєСЂС‹С‚СЊ РІСЃРµ С‡Р°СЃС‚Рё РІРѕРїСЂРѕСЃР° РєРѕСЂСЂРµРєС‚РЅРѕ.",
      fallback_type: "clarification",
      reply_type: "clarification_required"
    };
  }

  if (errorResults.length > 0 && okResults.length === 0 && partialResults.length === 0) {
    return {
      assistant_reply: "РќРµ СѓРґР°Р»РѕСЃСЊ РїРѕР»СѓС‡РёС‚СЊ РґР°РЅРЅС‹Рµ РёР· РєРѕРЅС‚СѓСЂР°. РџРѕРїСЂРѕР±СѓР№С‚Рµ РїРѕРІС‚РѕСЂРёС‚СЊ Р·Р°РїСЂРѕСЃ РёР»Рё СѓС‚РѕС‡РЅРёС‚СЊ С„РѕСЂРјСѓР»РёСЂРѕРІРєСѓ.",
      fallback_type: fallbackType,
      reply_type: "backend_error"
    };
  }

  if (partialResults.length > 0 && okResults.length === 0) {
    return {
      assistant_reply: composeExplainableAnswer(input, "partial"),
      fallback_type: "partial",
      reply_type: "partial_coverage"
    };
  }

  if (okResults.length === 0 && partialResults.length === 0 && emptyResults.length > 0) {
    return {
      assistant_reply: "РџРѕ Р·Р°РґР°РЅРЅРѕРјСѓ СѓСЃР»РѕРІРёСЋ РІ С‚РµРєСѓС‰РµРј СЃСЂРµР·Рµ РґР°РЅРЅС‹С… СЏРІРЅС‹С… РїСЂРѕР±Р»РµРјРЅС‹С… Р·Р°РїРёСЃРµР№ РЅРµ РЅР°Р№РґРµРЅРѕ.",
      fallback_type: fallbackType,
      reply_type: "empty_but_valid"
    };
  }

  const hasPartialCoverage =
    input.coverageReport.requirements_uncovered.length > 0 ||
    input.coverageReport.requirements_partially_covered.length > 0 ||
    input.coverageReport.clarification_needed_for.length > 0 ||
    input.coverageReport.out_of_scope_requirements.length > 0 ||
    input.groundingCheck.status === "partial" ||
    errorResults.length > 0 ||
    legacyLimitationReasonCodes.includes("weak_source_mapping") ||
    legacyLimitationReasonCodes.includes("missing_mechanism");

  if (okResults.length > 0 && hasPartialCoverage) {
    return {
      assistant_reply: composeExplainableAnswer(input, "partial"),
      fallback_type: "partial",
      reply_type: "partial_coverage"
    };
  }

  if (okResults.length > 0) {
    return {
      assistant_reply: composeExplainableAnswer(input, "full"),
      fallback_type: "none",
      reply_type: "factual_with_explanation"
    };
  }

  return {
    assistant_reply: "РџРѕ С‚РµРєСѓС‰РµРјСѓ Р·Р°РїСЂРѕСЃСѓ РЅРµ СѓРґР°Р»РѕСЃСЊ РїРѕСЃС‚СЂРѕРёС‚СЊ РѕР±РѕСЃРЅРѕРІР°РЅРЅС‹Р№ РѕС‚РІРµС‚. РЈС‚РѕС‡РЅРёС‚Рµ С„РѕСЂРјСѓР»РёСЂРѕРІРєСѓ Рё РїРѕРїСЂРѕР±СѓР№С‚Рµ СЃРЅРѕРІР°.",
    fallback_type: "unknown",
    reply_type: "backend_error"
  };
}


