import {
  buildEvidencePack,
  insertAfterEvidenceHeading,
  type EvidencePackItem,
} from "./evidence-pack.js";
import type { ExplicitCueRecallEngine } from "./explicit-cue-recall.js";
import {
  gatherAcrossReadSessions,
  resolveLcmReadSessionIds,
} from "./lcm-fallback-read.js";

import {
  unifiedDedupeAndRank,
  type RankedEvidenceItem,
} from "./recall-pipeline-stages.js";

export interface TargetedFactRecallOptions {
  engine: ExplicitCueRecallEngine | null | undefined;
  sessionId?: string;
  /**
   * Ordered, read-authorized LCM read key set (primary overlay → project/root
   * fallbacks). When present, evidence is gathered across EVERY key and merged
   * under this section's budget (#1505 codex P2). Falls back to `sessionId`.
   */
  sessionIds?: readonly (string | undefined)[];
  query: string;
  maxChars: number;
  maxItemChars?: number;
  maxSearchResults?: number;
  maxScanWindowTurns?: number;
  maxScanWindowTokens?: number;
  title?: string;
}

const DEFAULT_MAX_SEARCH_RESULTS = 48;
const DEFAULT_SCAN_WINDOW_TURNS = 8;
const DEFAULT_SCAN_WINDOW_TOKENS = 12_000;

export function shouldRecallTargetedFactEvidence(query: string): boolean {
  return classifyTargetedFactIntent(query) !== null;
}

export async function buildTargetedFactRecallSection(
  options: TargetedFactRecallOptions,
): Promise<string> {
  const budget = normalizePositiveInteger(options.maxChars);
  const maxResults = normalizePositiveInteger(
    options.maxSearchResults ?? DEFAULT_MAX_SEARCH_RESULTS,
  );
  if (!options.engine || budget <= 0 || !shouldRecallTargetedFactEvidence(options.query)) {
    return "";
  }
  if (maxResults <= 0) {
    return "";
  }

  // #1505 codex P2: gather candidates across the ordered LCM read key set
  // (primary overlay → project/root fallbacks) and UNION them into the existing
  // rank/dedupe/budget pass, so stronger project-fallback evidence is not masked
  // by a weak primary-key hit. `gatherAcrossReadSessions` isolates a per-key read
  // failure so a corrupt/locked fallback index can't discard the primary key's
  // evidence; the single-key path collects exactly one search+scan pair and
  // propagates a failure as before — byte-for-byte the pre-#1505 behavior.
  const collected: EvidencePackItem[] = [];
  await gatherAcrossReadSessions(resolveLcmReadSessionIds(options), async (sessionId) => {
    const scoped = { ...options, sessionId };
    collected.push(...(await collectTargetedFactSearchItems(scoped)));
    collected.push(...(await collectTargetedFactScanItems(scoped)));
  });
  const ranked = rankAndDedupeTargetedFactItems(
    collected,
    options.query,
  ).slice(0, maxResults);

  const title = options.title ?? "Targeted fact evidence";
  const summary = buildTargetedFactSummary(ranked, options.query);
  const summaryInsert = summary ? `\n\n${summary}` : "";
  const evidenceBudget = summaryInsert
    ? Math.max(0, budget - summaryInsert.length)
    : budget;
  const evidence = buildEvidencePack(ranked, {
    title,
    maxChars: evidenceBudget,
    maxItemChars: options.maxItemChars,
    query: buildTargetedFactQuery(options.query),
  });
  if (!summary) {
    return evidence;
  }
  if (!evidence) {
    return clipTextToBudget(`## ${title}${summaryInsert}`, budget);
  }
  return insertAfterEvidenceHeading(evidence, title, summaryInsert);
}

async function collectTargetedFactSearchItems(
  options: TargetedFactRecallOptions,
): Promise<EvidencePackItem[]> {
  const engine = options.engine;
  if (!engine) return [];

  const results = await engine.searchContextFull(
    buildTargetedFactQuery(options.query),
    normalizePositiveInteger(options.maxSearchResults ?? DEFAULT_MAX_SEARCH_RESULTS),
    options.sessionId,
  );
  const searchWindowTurns = Math.max(
    1,
    normalizePositiveInteger(options.maxScanWindowTurns ?? DEFAULT_SCAN_WINDOW_TURNS),
  );
  const searchWindowBefore = Math.floor((searchWindowTurns - 1) / 2);
  const searchWindowAfter = Math.ceil((searchWindowTurns - 1) / 2);
  const searchWindowTokens = Math.max(
    1,
    normalizePositiveInteger(options.maxScanWindowTokens ?? DEFAULT_SCAN_WINDOW_TOKENS),
  );
  const items: EvidencePackItem[] = [];
  const seen = new Set<string>();

  for (const result of results) {
    const expanded = await engine.expandContext(
      result.session_id,
      Math.max(0, result.turn_index - searchWindowBefore),
      result.turn_index + searchWindowAfter,
      searchWindowTokens,
    );
    const searchHit: EvidencePackItem = {
      id: `${result.session_id}:${result.turn_index}`,
      sessionId: result.session_id,
      turnIndex: result.turn_index,
      role: result.role,
      content: result.content,
      ...(typeof result.score === "number" ? { score: result.score } : {}),
    };
    const candidates: EvidencePackItem[] = expanded.map((message) => ({
      id: `${result.session_id}:${message.turn_index}`,
      sessionId: result.session_id,
      turnIndex: message.turn_index,
      role: message.role,
      content: message.content,
      ...(message.turn_index === result.turn_index &&
      typeof result.score === "number"
        ? { score: result.score }
        : {}),
    }));
    const hitIndex = candidates.findIndex((candidate) =>
      candidate.turnIndex === result.turn_index
    );
    if (hitIndex >= 0) {
      candidates[hitIndex] = searchHit;
    } else {
      candidates.unshift(searchHit);
    }

    for (const candidate of candidates) {
      const candidateId = candidate.id ?? (
        candidate.sessionId && typeof candidate.turnIndex === "number"
          ? `${candidate.sessionId}:${candidate.turnIndex}`
          : undefined
      );
      if (candidateId && seen.has(candidateId)) continue;
      if (!isTargetedFactEvidence(candidate.content, options.query)) continue;
      if (candidateId) seen.add(candidateId);
      items.push(candidate);
    }
  }

  return items;
}

async function collectTargetedFactScanItems(
  options: TargetedFactRecallOptions,
): Promise<EvidencePackItem[]> {
  const engine = options.engine;
  if (!engine?.getStats || !options.sessionId) return [];

  const stats = await engine.getStats(options.sessionId);
  const maxTurn = typeof stats.maxTurnIndex === "number"
    ? stats.maxTurnIndex
    : stats.totalMessages - 1;
  if (maxTurn < 0) return [];

  const windowTurns = Math.max(
    1,
    normalizePositiveInteger(options.maxScanWindowTurns ?? DEFAULT_SCAN_WINDOW_TURNS),
  );
  const windowTokens = Math.max(
    1,
    normalizePositiveInteger(options.maxScanWindowTokens ?? DEFAULT_SCAN_WINDOW_TOKENS),
  );
  const items: EvidencePackItem[] = [];

  const fromTurn = Math.max(0, maxTurn - windowTurns + 1);
  const messages = await engine.expandContext(
    options.sessionId,
    fromTurn,
    maxTurn,
    windowTokens,
  );
  for (const message of messages) {
    if (!isTargetedFactEvidence(message.content, options.query)) continue;
    items.push({
      id: `${options.sessionId}:${message.turn_index}`,
      sessionId: options.sessionId,
      turnIndex: message.turn_index,
      role: message.role,
      content: message.content,
    });
  }

  return items;
}

/**
 * Issue #1539 PR3: targeted-fact now routes dedup/score/threshold/sort through
 * the unified spine (`unifiedDedupeAndRank`). The divergence that lived inline
 * here — append-normalized-numeric-cues content transform, relevance scoring on
 * ORIGINAL content, default DESC turn-index ordering with -1 sentinel for
 * missing turns — is now declared config on the spine. The score and
 * appendNormalizedNumericCues helpers stay in this module because they are
 * genuinely per-tier policy; only the duplicated dedup/sort/threshold machinery
 * moved out.
 *
 * Behavior is byte-for-byte identical to the inline implementation it replaces
 * (the spine was extracted to match it exactly; see recall-pipeline-stages.ts).
 * The targeted-fact-recall.test.ts suite (42 cases, incl. the search/scan
 * ordering and summary insertion tests) is the characterization.
 */
function rankAndDedupeTargetedFactItems(
  items: EvidencePackItem[],
  query: string,
): RankedEvidenceItem[] {
  return unifiedDedupeAndRank(items, {
    query,
    intents: [],
    scoreEvidence: (item, q) => scoreTargetedFactEvidence(item, q),
    transformContent: (content) => appendNormalizedNumericCues(content),
  });
}

function scoreTargetedFactEvidence(item: EvidencePackItem, query: string): number {
  const content = item.content.toLowerCase();
  const normalizedQuery = query.toLowerCase();
  let score = 0;

  if (item.role === "user") score += 8;
  if (item.role === "assistant") score += 2;
  if (hasMoneyOrPercent(item.content)) score += 8;
  if (/\b\d[\d,]*(?:\.\d+)?\s?%/.test(content)) score += 5;
  if (/\b(?:finally|reached|updated|adjusted|increased|decreased|now|current|currently|latest|most recent)\b/.test(content)) {
    score += 7;
  }
  if (isCacheTtlQuery(normalizedQuery)) {
    if (isCacheTtlEvidenceText(content)) score += 60;
    if (/\b7200\s+seconds?\b/.test(content)) score += 30;
    if (/\b(?:redis|cache|ttl|time-to-live|time to live|diffusion features?)\b/.test(content)) {
      score += 12;
    }
  }

  if (isGameCachingAuthenticationToolCountQuery(normalizedQuery)) {
    if (isGameCachingAuthenticationToolCountEvidenceText(content)) score += 70;
    if (/\bredis\s*6\.2\b/.test(content)) score += 45;
    if (/\bphaser\s*3\.55\b/.test(content)) score += 45;
    if (/\bnode\.?js\b/.test(content)) score += 28;
    if (/\bexpress(?:\.js)?\b/.test(content)) score += 28;
    if (/\b(?:jwt|jsonwebtoken)\b/.test(content)) score += 28;
    if (/\bdocker\b/.test(content)) score += 20;
    if (/\b(?:authentication|auth|refresh token|token rotation)\b/.test(content)) {
      score += 18;
    }
    if (/\b(?:game state snapshots?|game caching|phaser|2d rendering)\b/.test(content)) {
      score += 18;
    }
  }

  if (isGeometryTypeCountQuery(normalizedQuery)) {
    if (isGeometryTypeCountEvidenceText(content)) score += 70;
    if (/\beuclidean\b/.test(content)) score += 24;
    if (/\bhyperbolic\b/.test(content)) score += 24;
    if (/\bspherical\b/.test(content)) score += 24;
    if (/\b(?:parallel lines?|triangle angle sums?|angle sums?|geometr(?:y|ies))\b/.test(content)) {
      score += 18;
    }
  }

  if (isBitcoinInvestmentPlatformQuery(normalizedQuery)) {
    if (isBitcoinInvestmentPlatformEvidenceText(content)) score += 80;
    if (/\bbitcoin\b/.test(content)) score += 28;
    if (/\bbinance\b/.test(content)) score += 36;
    if (/\$\s?500\b|\b500\s+dollars?\b/.test(content)) score += 42;
  }

  if (isCryptoFeeTotalQuery(normalizedQuery)) {
    if (isCryptoFeeEvidenceText(content)) score += 70;
    if (/\bethereum\b/.test(content)) score += 18;
    if (/\bwallet transfer\b/.test(content)) score += 20;
    if (/\bnft\b/.test(content)) score += 18;
    if (/\bgas fees?\b/.test(content)) score += 20;
    if (/\$\s?5\b|\b5\s+dollars?\b/.test(content)) score += 18;
    if (/\$\s?2\.50\b|\b2\.50\s+dollars?\b/.test(content)) score += 18;
    if (/\$\s?10\b|\b10\s+dollars?\b/.test(content)) score += 18;
  }

  if (isPopulationGrowthRateQuery(normalizedQuery)) {
    if (isPopulationGrowthRateEvidenceText(content)) score += 70;
    if (/\bk\s*=\s*0?\.035\b/.test(content)) score += 60;
    if (/\b(?:growth rate|population growth model|exponential growth model)\b/.test(content)) {
      score += 24;
    }
    if (/\b(?:i'?ve been practicing|i was using|using)\b/.test(content)) {
      score += 18;
    }
    if (/\br\s*=\s*0?\.1\b|\bk\s*=\s*0?\.03\b/.test(content)) {
      score -= 12;
    }
  }

  const queryNumbers = extractNumericCueTokens(query);
  for (const number of queryNumbers) {
    if (new RegExp(`\\b${escapeRegExp(number)}\\b`).test(content)) {
      score += 12;
    }
  }

  if (/\bemergency fund\b/.test(normalizedQuery)) {
    if (/\bemergency fund\b/.test(content)) score += 18;
    if (/\b(?:saved|reached|goal|fund)\b/.test(content)) score += 8;
  }

  if (/\bholiday\b.*\bgifts?\b|\bgifts?\b.*\bholiday\b/.test(normalizedQuery)) {
    if (/\bholiday\b.*\bgifts?\b|\bgifts?\b.*\bholiday\b/.test(content)) {
      score += 18;
    }
    if (/\bbudget\b/.test(content)) score += 8;
    if (/\b(?:adjusted|cap|capped|total)\b/.test(content)) score += 6;
  }

  if (/\bfinancial goals?\b/.test(normalizedQuery)) {
    if (/\bfinancial goals?\b/.test(content)) score += 10;
    if (/\b(?:budget|fund|savings?|allocation|goal|expenses?|distribution|shift|shifting)\b/.test(content)) {
      score += 8;
    }
  }

  if (isAddressQuery(normalizedQuery)) {
    if (hasStreetAddress(item.content)) score += 48;
    if (/\b(?:my place|where i live|address|live|home|apartment|location)\b/.test(content)) {
      score += 22;
    }
    if (/\b(?:street|st\.?|avenue|ave\.?|road|rd\.?|drive|dr\.?|lane|ln\.?|boulevard|blvd\.?|way|court|ct\.?|place|pl\.?)\b/.test(content)) {
      score += 12;
    }
  }

  if (isBakingPurchaseTotalQuery(normalizedQuery)) {
    if (isBakingPurchaseTotalEvidenceText(content)) score += 48;
    if (/\bkitchenaid\b|\bstand mixer\b|\bmixer\b/.test(content)) score += 24;
    if (/\borganic almond flour\b|\balmond flour\b/.test(content)) score += 24;
    if (/\b(?:spent|spending|cost|worth|investment|paid)\b/.test(content)) score += 10;
  }

  if (isTripEquipmentSavingsTotalQuery(normalizedQuery)) {
    if (isTripEquipmentSavingsEvidenceText(content)) score += 48;
    if (/\bgoalie mask\b/.test(content)) score += 28;
    if (/\btrip\b/.test(content)) score += 20;
    if (/\b(?:future equipment|equipment needs?|savings?)\b/.test(content)) {
      score += 20;
    }
    if (/\b(?:split the resources|set aside|trip budget under|spent)\b/.test(content)) {
      score += 16;
    }
    if (/\b(?:total trip budget|need additional funds|adjust the budget)\b/.test(content)) {
      score -= 28;
    }
  }

  if (isMvpDeadlineRemainingQuery(normalizedQuery)) {
    if (isMvpDeadlineEvidenceText(content)) score += 48;
    if (/\bmvp\b/.test(content)) score += 18;
    if (/\b(?:started coding|start(?:ed)? development|begin coding|coding)\b/.test(content)) {
      score += 18;
    }
    if (/\b(?:may\s+1|june\s+12|6 weeks?|six weeks?|42\s+days?)\b/.test(content)) {
      score += 24;
    }
  }

  if (isPurchaseBudgetQuery(normalizedQuery)) {
    if (isPurchaseBudgetEvidenceText(content)) score += 42;
    if (extractPurchaseBudgetEvidenceSegments(content).length > 0) score += 24;
    if (isAccessoryBudgetOnlyText(content)) score -= 48;
    if (/\b(?:budget|ceiling|cap|limit)\b/.test(content)) score += 16;
    if (/\b(?:phone|smartphone|device|laptop|camera|battery|photography|gaming)\b/.test(content)) {
      score += 12;
    }
    if (/\b(?:adjusted|increased|updated|raised|new budget|now|recently|current|currently)\b/.test(content)) {
      score += 24;
    }
    if (/\b(?:adjusted|increased|updated|raised)\b[^.?\n]{0,120}\bbudget\b[^.?\n]{0,80}\$\s?\d/.test(content)) {
      score += 32;
    }
    if (/\b(?:original|previous|initial|old)\b/.test(content)) {
      score -= 8;
    }
  }

  if (isTutoringGoalDurationQuery(normalizedQuery)) {
    if (isTutoringGoalDurationEvidenceText(content)) score += 42;
    if (/\b(?:twice weekly|tutoring sessions?|sessions?)\b/.test(content)) score += 18;
    if (/\b(?:march\s+20|june\s+1|80%|80\s+percent|math scores?)\b/.test(content)) {
      score += 18;
    }
    if (/\b(?:starting|goal|by|reach|improve)\b/.test(content)) score += 10;
  }

  if (isSalaryQuery(normalizedQuery)) {
    if (isSalaryEvidenceText(content)) score += 44;
    if (/\b(?:salary|earn|raise|compensation)\b/.test(content)) score += 18;
    if (/\b(?:cad|annually|annual|senior engineer|saint pierre manufacturing ltd|manufacturing)\b/.test(content)) {
      score += 14;
    }
    if (/\b(?:recent raise|raised to|updated|now|current|currently)\b/.test(content)) {
      score += 10;
    }
    if (/\bsaint pierre manufacturing ltd\b/.test(normalizedQuery)) {
      if (/\bsaint pierre manufacturing ltd\b/.test(content)) score += 28;
      if (/\brecent raise\b|\braise to\b/.test(content)) score += 18;
    } else {
      if (/\bearn(?:s|ed)? approximately\b|\bannually\b/.test(content)) score += 24;
      if (/\brecent raise\b|\braise to\b/.test(content)) score -= 6;
    }
  }

  if (isTrainingCostQuery(normalizedQuery)) {
    if (isTrainingCostEvidenceText(content)) score += 46;
    if (/\b(?:training|course|coursera|enrolled|enrolling)\b/.test(content)) {
      score += 20;
    }
    if (/\b(?:cost|costing|spending|investment|afford|fee|price)\b/.test(content)) {
      score += 18;
    }
    if (/\b(?:leadership training|12-week|starting february 5|via coursera)\b/.test(content)) {
      score += 18;
    }
    if (/\b(?:costs typically range|examples?|sample budget|budget allocation)\b/.test(content)) {
      score -= 24;
    }
  }

  if (isPortfolioFeeTotalQuery(normalizedQuery)) {
    if (isPortfolioFeeEvidenceText(content, normalizedQuery)) score += 48;
    if (/\b(?:fee|fees|transaction fees?|subscription fees?)\b/.test(content)) {
      score += 18;
    }
    if (/\b(?:rebalancing|art fund acquisition|wealthfront subscription|wealthfront)\b/.test(content)) {
      score += 24;
    }
    if (/\b(?:vanguard|masterworks|tax-loss harvesting)\b/.test(content)) {
      score += 10;
    }
  }

  if (isRetirementContributionQuery(normalizedQuery)) {
    if (isRetirementContributionEvidenceText(content)) score += 48;
    if (/\broth ira\b/.test(content)) score += 22;
    if (/\b(?:contribution|contributions|contribute|contributing)\b/.test(content)) score += 18;
    if (/\bmonthly\b/.test(content)) score += 16;
    if (/\b(?:increased|starting|current|currently|now|recently|latest)\b/.test(content)) {
      score += 18;
    }
  }

  if (isConfidenceBoostTotalQuery(normalizedQuery)) {
    if (isConfidenceBoostEvidenceText(content, normalizedQuery)) score += 48;
    if (/\b(?:co-leading|co-hosting|co-leading or co-hosting|support group|writing circle)\b/.test(content)) {
      score += 20;
    }
    if (/\bpatricia\b/.test(normalizedQuery) && /\bpatricia\b/.test(content)) {
      score += 20;
    }
    if (/\b(?:confidence boost|boost(?:ed)? (?:my|your)? confidence|boost in (?:my|your) confidence)\b/.test(content)) {
      score += 18;
    }
  }

  if (/\b(?:coverage|percentage|percent|accuracy|score|metric|progress)\b/.test(normalizedQuery)) {
    if (/\b(?:coverage|percentage|percent|accuracy|scor(?:e|ed|ing)|metric|progress)\b/.test(content)) {
      score += 12;
    }
    if (/\b(?:improved|reached|currently|current|latest|recently|as of|now)\b/.test(content)) {
      score += 8;
    }
    if (/\b(?:recently improved|improved to|updated to|latest|most recent|now)\b/.test(content)) {
      score += 10;
    }
    if (/\bapi integration\b/.test(normalizedQuery)) {
      if (/\bapi integration\b/.test(content)) score += 45;
      if (/\bcore modules?\b/.test(content)) score -= 24;
    }
    if (/\btest coverage\b/.test(normalizedQuery)) {
      if (/\b(?:unit )?test coverage\b/.test(content)) score += 24;
      if (/\bcoverage on core modules?\b/.test(content)) score -= 16;
    }
    if (/\b(?:quiz|practice test|score)\b/.test(normalizedQuery) && /\b(?:quiz|practice test|test score|scor(?:e|ed|ing))\b/.test(content)) {
      score += 16;
    }
    if (/\b(?:induction|number theory|proofs?)\b/.test(normalizedQuery)) {
      if (/\b(?:induction|number theory|discrete math|proofs?|inequalit(?:y|ies)|divisibility)\b/.test(content)) {
        score += 12;
      }
      if (/\b(?:practice test|quiz score|score increased|increased to)\b/.test(content)) {
        score += 10;
      }
    }
    if (/\b(?:latest|most recent|recently|current)\b/.test(normalizedQuery) && /\b(?:increased to|improved to|score increased|latest|most recent|recently)\b/.test(content)) {
      score += 12;
    }
  }

  if (/\b(?:word count|words?|editing challenge|writing progress)\b/.test(normalizedQuery)) {
    if (/\b(?:word count|words?|editing challenge|writing progress|filler words|clarity)\b/.test(content)) {
      score += 16;
    }
    if (/\b(?:increased|adjusted|starting|started|completed|from|to)\b/.test(content)) {
      score += 8;
    }
  }

  score += countFocusedTermOverlap(content, normalizedQuery) * 2;
  if (
    typeof item.score === "number" &&
    Number.isFinite(item.score) &&
    !/\b(?:coverage|percentage|percent|accuracy|score|metric|progress)\b/.test(normalizedQuery)
  ) {
    score += Math.min(5, Math.max(0, item.score / 20));
  }
  return score;
}

function buildTargetedFactQuery(query: string): string {
  return [
    query,
    "budget fund saved savings money total goal reached current amount adjusted increased decreased dollars percent",
    "purchase budget ceiling cap limit phone smartphone device camera battery life photography gaming adjusted updated current most recent latest",
    "holiday gift budget emergency fund reached saved total monthly expenses financial goals allocation",
    "date timeline duration days after before by on June August February November",
    "time passed daily work project milestone March 1 March 15 detection pipeline integrated ready for testing two weeks 14 days",
    "tutoring sessions twice weekly starting March 20 math score goal 80% June 1 approximately 11 weeks",
    "salary annual salary senior engineer Saint Pierre Manufacturing Ltd earn approximately raise CAD annually compensation current job",
    "training cost course enrolled enrollment leadership training Coursera costing spending fee price $350 12-week starting February 5",
    "portfolio fees total rebalancing transaction fees art fund acquisition Wealthfront subscription fees $75 $120 $50 total $245 Vanguard Masterworks",
    "Roth IRA monthly contribution contributions contribute retirement savings increased starting current latest $475 Fidelity monthly",
    "confidence boost total co-leading co-hosting Patricia support group writing circle boosted my confidence boost in your confidence 40% 30% 140% raw mentions de-duplicated user-reported",
    "address location where I live my place home apartment street avenue road drive lane 1423 Maple Street",
    "KitchenAid mixer stand mixer organic almond flour spent spending combined total $399 $25 $424 424 dollars",
    "goalie mask delayed summer trip Courtney future equipment savings split resources set aside combined total $580 $200 $380 totaling 1160 dollars",
    "MVP development deadline started coding May 1 2024 June 12 2024 6-week development period 42 days 0 days left",
    "time between TF-IDF vectorization content-based filtering Sprint 2 beta release February 25 internal testing early November approximately 135 days",
    "time between constrained optimization Lagrange multiplier and gradient vector directional derivative example January 10 February 3 24 days",
    "Redis cache TTL time-to-live 7200 seconds diffusion features API response time cache configuration performance optimization",
    "game caching authentication technologies tools count Redis 6.2 Phaser 3.55 Node.js Express.js JWT jsonwebtoken Docker refresh token rotation Redis session caching game state snapshots",
    "geometry types count Three types Euclidean hyperbolic spherical geometries parallel lines triangle angle sums",
    "Bitcoin investment amount platform $500 invested Bitcoin Binance January 20 cryptocurrency exchange",
    "Ethereum purchase wallet transfer NFT purchase combined transaction fees $5 $2.50 $10 gas fees total $17.50",
    "population model growth rate k=0.035 exponential growth model population growth model constant growth rate using k value",
    "weekly word count goal words increased from to writing progress editing challenge 30-day 15-day April May",
    "coverage percentage percent accuracy score metric progress improved reached current latest most recent",
    "quiz score practice test induction number theory discrete math proofs most recently latest increased improved percent",
    ...extractNumericCueTokens(query).map((number) => `$${number} ${number}% ${number} dollars`),
  ].join(" ");
}

function isTargetedFactEvidence(content: string, query: string): boolean {
  const normalizedContent = content.toLowerCase();
  const normalizedQuery = query.toLowerCase();
  if (isAddressQuery(normalizedQuery)) {
    return isAddressEvidenceText(content, normalizedQuery);
  }

  if (isCacheTtlQuery(normalizedQuery)) {
    return isCacheTtlEvidenceText(normalizedContent);
  }

  if (isGameCachingAuthenticationToolCountQuery(normalizedQuery)) {
    return isGameCachingAuthenticationToolCountEvidenceText(normalizedContent);
  }

  if (isGeometryTypeCountQuery(normalizedQuery)) {
    return isGeometryTypeCountEvidenceText(normalizedContent);
  }

  if (isBitcoinInvestmentPlatformQuery(normalizedQuery)) {
    return isBitcoinInvestmentPlatformEvidenceText(normalizedContent);
  }

  if (isCryptoFeeTotalQuery(normalizedQuery)) {
    return isCryptoFeeEvidenceText(normalizedContent);
  }

  if (isPopulationGrowthRateQuery(normalizedQuery)) {
    return isPopulationGrowthRateEvidenceText(normalizedContent);
  }

  const hasNumericFact = hasMoneyOrPercent(content) ||
    hasWritingNumericFact(content) ||
    hasProjectDurationFact(content) ||
    hasTutoringGoalDurationFact(content) ||
    hasSalaryFact(content) ||
    hasCacheTtlFact(content) ||
    hasPopulationGrowthRateFact(content) ||
    /\b(?:distribution|allocation|shift|shifting|spreadsheet|excel|one-time|recurring)\b/.test(
      normalizedContent,
    );
  if (!hasNumericFact) {
    return false;
  }

  if (/\bholiday\b.*\bgifts?\b|\bgifts?\b.*\bholiday\b/.test(normalizedQuery)) {
    return /\bholiday\b.*\bgifts?\b|\bgifts?\b.*\bholiday\b/.test(
      normalizedContent,
    ) && /\bbudget\b/.test(normalizedContent);
  }

  if (/\bemergency fund\b/.test(normalizedQuery)) {
    return /\bemergency fund\b/.test(normalizedContent) &&
      /\b(?:saved|reached|goal|fund|percent|%)\b/.test(normalizedContent);
  }

  if (/\bfinancial goals?\b/.test(normalizedQuery)) {
    return /\b(?:budget|fund|savings?|allocation|goal|goals|expenses?|distribution|shift|shifting)\b/.test(
      normalizedContent,
    );
  }

  if (isBakingPurchaseTotalQuery(normalizedQuery)) {
    return isBakingPurchaseTotalEvidenceText(normalizedContent);
  }

  if (isTripEquipmentSavingsTotalQuery(normalizedQuery)) {
    return isTripEquipmentSavingsEvidenceText(normalizedContent);
  }

  if (isMvpDeadlineRemainingQuery(normalizedQuery)) {
    return isMvpDeadlineEvidenceText(normalizedContent);
  }

  if (isPurchaseBudgetQuery(normalizedQuery)) {
    return isPurchaseBudgetEvidenceText(normalizedContent);
  }

  if (isTutoringGoalDurationQuery(normalizedQuery)) {
    return isTutoringGoalDurationEvidenceText(normalizedContent);
  }

  if (isSalaryQuery(normalizedQuery)) {
    return isSalaryEvidenceText(normalizedContent);
  }

  if (isTrainingCostQuery(normalizedQuery)) {
    return isTrainingCostEvidenceText(normalizedContent);
  }

  if (isPortfolioFeeTotalQuery(normalizedQuery)) {
    return isPortfolioFeeEvidenceText(normalizedContent, normalizedQuery);
  }

  if (isRetirementContributionQuery(normalizedQuery)) {
    return isRetirementContributionEvidenceText(normalizedContent);
  }

  if (isConfidenceBoostTotalQuery(normalizedQuery)) {
    return isConfidenceBoostEvidenceText(normalizedContent, normalizedQuery);
  }

  if (/\b(?:coverage|percentage|percent|accuracy|score|metric|progress)\b/.test(normalizedQuery)) {
    return /\b(?:coverage|percentage|percent|accuracy|scor(?:e|ed|ing)|metric|progress)\b/.test(
      normalizedContent,
    );
  }

  if (/\b(?:word count|words?|editing challenge|writing progress)\b/.test(normalizedQuery)) {
    return /\b(?:word count|words?|editing challenge|writing progress|filler words|clarity)\b/.test(
      normalizedContent,
    ) && hasWritingNumericFact(content);
  }

  if (/\b(?:daily work|time passed|detection model|detection pipeline|ready for testing|project deadline|mvp|development deadline)\b/.test(normalizedQuery)) {
    return /\b(?:march\s+1|march\s+15|two weeks?|14\s+days?|daily work|detection pipeline|ready for testing|project deadline|mvp|started coding|start(?:ed)? development|may\s+1|june\s+12|6 weeks?|six weeks?)\b/.test(
      normalizedContent,
    );
  }

  if (/\b(?:tf-?idf|beta release|internal testing|content-based filtering)\b/.test(normalizedQuery)) {
    return /\b(?:tf-?idf|content-based filtering|sprint\s+2|beta release|february\s+25|internal users?|internal testing|early november|135\s+days?)\b/.test(
      normalizedContent,
    );
  }

  const overlap = countFocusedTermOverlap(normalizedContent, normalizedQuery);
  return overlap >= 1;
}

function shouldRecallFinancialFact(query: string): boolean {
  const normalized = query.toLowerCase();
  return (
    /\b(?:budget|fund|saved|saving|savings|money|financial|expenses?|spending|gifts?|goal|goals?)\b/.test(
      normalized,
    ) &&
    /\b(?:what|how much|total|current|reached|goal|budget|saved|goals?)\b/.test(
      normalized,
    )
  );
}

function shouldRecallPurchaseBudgetFact(query: string): boolean {
  const normalized = query.toLowerCase();
  return isPurchaseBudgetQuery(normalized) &&
    /\b(?:what|how much|current|latest|most recent|set|ceiling|cap|limit|budget)\b/.test(
      normalized,
    );
}

function shouldRecallMetricFact(query: string): boolean {
  const normalized = query.toLowerCase();
  return /\b(?:coverage|percentage|percent|accuracy|score|metric|progress|test|tests)\b/.test(
    normalized,
  ) && /\b(?:what|how much|current|latest|most recent|percentage|percent)\b/.test(
    normalized,
  );
}

function shouldRecallWritingMetricFact(query: string): boolean {
  const normalized = query.toLowerCase();
  return /\b(?:word count|words?|editing challenge|writing progress|filler words|clarity)\b/.test(
    normalized,
  ) && /\b(?:how much|how many|increase|increased|between|from|until|completed|started|passed)\b/.test(
    normalized,
  );
}

function shouldRecallProjectDurationFact(query: string): boolean {
  const normalized = query.toLowerCase();
  return /\b(?:how much time|time passed|how long|duration|weeks?|days?)\b/.test(normalized) &&
    /\b(?:daily work|committed|detection model|detection pipeline|integrated|ready for testing|project|tf-?idf|content-based filtering|beta release|internal testing|sprint|mvp|development period|deadline|coding|constrained optimization|gradient vector|directional derivative|lagrange multipliers?)\b/.test(normalized);
}

function shouldRecallTutoringGoalDurationFact(query: string): boolean {
  const normalized = query.toLowerCase();
  return isTutoringGoalDurationQuery(normalized);
}

function shouldRecallSalaryFact(query: string): boolean {
  return isSalaryQuery(query.toLowerCase());
}

function shouldRecallTrainingCostFact(query: string): boolean {
  return isTrainingCostQuery(query.toLowerCase());
}

function shouldRecallPortfolioFeeTotalFact(query: string): boolean {
  return isPortfolioFeeTotalQuery(query.toLowerCase());
}

function shouldRecallRetirementContributionFact(query: string): boolean {
  return isRetirementContributionQuery(query.toLowerCase());
}

function shouldRecallConfidenceBoostTotalFact(query: string): boolean {
  return isConfidenceBoostTotalQuery(query.toLowerCase());
}

function shouldRecallCacheTtlFact(query: string): boolean {
  return isCacheTtlQuery(query.toLowerCase());
}

function shouldRecallGameCachingAuthenticationToolCountFact(query: string): boolean {
  return isGameCachingAuthenticationToolCountQuery(query.toLowerCase());
}

function shouldRecallGeometryTypeCountFact(query: string): boolean {
  return isGeometryTypeCountQuery(query.toLowerCase());
}

function shouldRecallBitcoinInvestmentPlatformFact(query: string): boolean {
  return isBitcoinInvestmentPlatformQuery(query.toLowerCase());
}

function shouldRecallCryptoFeeTotalFact(query: string): boolean {
  return isCryptoFeeTotalQuery(query.toLowerCase());
}

function shouldRecallPopulationGrowthRateFact(query: string): boolean {
  return isPopulationGrowthRateQuery(query.toLowerCase());
}

function shouldRecallAddressFact(query: string): boolean {
  return isAddressQuery(query.toLowerCase());
}

function shouldRecallBakingPurchaseTotalFact(query: string): boolean {
  return isBakingPurchaseTotalQuery(query.toLowerCase());
}

function shouldRecallTripEquipmentSavingsTotalFact(query: string): boolean {
  return isTripEquipmentSavingsTotalQuery(query.toLowerCase());
}

function classifyTargetedFactIntent(
  query: string,
): "financial" | "metric" | "writing_metric" | "project_duration" | "purchase_budget" | "tutoring_duration" | "training_cost" | "portfolio_fee_total" | "retirement_contribution" | "salary" | "confidence_boost_total" | "game_auth_tool_count" | "geometry_type_count" | "bitcoin_investment_platform" | "crypto_fee_total" | "population_growth_rate" | "cache_ttl" | "address" | "baking_purchase_total" | "trip_equipment_total" | null {
  if (shouldRecallAddressFact(query)) return "address";
  if (shouldRecallBakingPurchaseTotalFact(query)) return "baking_purchase_total";
  if (shouldRecallTripEquipmentSavingsTotalFact(query)) return "trip_equipment_total";
  if (shouldRecallTutoringGoalDurationFact(query)) return "tutoring_duration";
  if (shouldRecallPurchaseBudgetFact(query)) return "purchase_budget";
  if (shouldRecallSalaryFact(query)) return "salary";
  if (shouldRecallTrainingCostFact(query)) return "training_cost";
  if (shouldRecallPortfolioFeeTotalFact(query)) return "portfolio_fee_total";
  if (shouldRecallRetirementContributionFact(query)) return "retirement_contribution";
  if (shouldRecallConfidenceBoostTotalFact(query)) return "confidence_boost_total";
  if (shouldRecallGameCachingAuthenticationToolCountFact(query)) return "game_auth_tool_count";
  if (shouldRecallGeometryTypeCountFact(query)) return "geometry_type_count";
  if (shouldRecallBitcoinInvestmentPlatformFact(query)) return "bitcoin_investment_platform";
  if (shouldRecallCryptoFeeTotalFact(query)) return "crypto_fee_total";
  if (shouldRecallPopulationGrowthRateFact(query)) return "population_growth_rate";
  if (shouldRecallCacheTtlFact(query)) return "cache_ttl";
  if (shouldRecallFinancialFact(query)) return "financial";
  if (shouldRecallMetricFact(query)) return "metric";
  if (shouldRecallWritingMetricFact(query)) return "writing_metric";
  if (shouldRecallProjectDurationFact(query)) return "project_duration";
  return null;
}

function appendNormalizedNumericCues(content: string): string {
  const cues = collectNormalizedNumericCues(content);
  if (cues.length === 0) return content;
  return `${content}\n\nNormalized numeric cues: ${cues.join("; ")}.`;
}

function buildTargetedFactSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  return [
    buildAddressFactSummary(items, query),
    buildBakingPurchaseTotalSummary(items, query),
    buildTripEquipmentSavingsTotalSummary(items, query),
    buildEmergencyFundDurationSummary(items, query),
    buildPurchaseBudgetSummary(items, query),
    buildPercentageScoreDeltaSummary(items, query),
    buildLatestPercentageScoreSummary(items, query),
    buildWordCountDeltaSummary(items, query),
    buildEditingChallengeDurationSummary(items, query),
    buildTutoringGoalDurationSummary(items, query),
    buildTrainingCostSummary(items, query),
    buildPortfolioFeeTotalSummary(items, query),
    buildRetirementContributionSummary(items, query),
    buildSalaryFactSummary(items, query),
    buildConfidenceBoostTotalSummary(items, query),
    buildGameCachingAuthenticationToolCountSummary(items, query),
    buildGeometryTypeCountSummary(items, query),
    buildBitcoinInvestmentPlatformSummary(items, query),
    buildCryptoFeeTotalSummary(items, query),
    buildPopulationGrowthRateSummary(items, query),
    buildCacheTtlSummary(items, query),
    buildProjectDurationSummary(items, query),
  ].filter(Boolean).join(" ");
}

function buildAddressFactSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isAddressQuery(normalizedQuery)) return "";

  const candidates = items
    .flatMap((item) => extractStreetAddresses(item.content).map((address) => ({
      address,
      turn: typeof item.turnIndex === "number" ? item.turnIndex : null,
      specificity: scoreAddressEvidenceSpecificity(item.content.toLowerCase(), normalizedQuery),
    })))
    .sort((left, right) => {
      if (right.specificity !== left.specificity) return right.specificity - left.specificity;
      if (left.turn !== null && right.turn !== null && left.turn !== right.turn) {
        return right.turn - left.turn;
      }
      if (left.turn !== null && right.turn === null) return -1;
      if (left.turn === null && right.turn !== null) return 1;
      return left.address.localeCompare(right.address);
    });
  const selected = candidates[0];
  return selected ? `Address evidence: ${selected.address}.` : "";
}

function buildBakingPurchaseTotalSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isBakingPurchaseTotalQuery(normalizedQuery)) return "";

  const checkpoints = extractBakingPurchaseCheckpoints(items);
  const mixer = selectBakingPurchaseCheckpoint(checkpoints, "kitchenaid_mixer");
  const almondFlour = selectBakingPurchaseCheckpoint(checkpoints, "organic_almond_flour");
  if (!mixer || !almondFlour) return "";

  const total = mixer.amount + almondFlour.amount;
  return `Computed baking purchase total: ${formatNumber(total)} dollars ($${formatNumber(mixer.amount)} KitchenAid mixer + $${formatNumber(almondFlour.amount)} organic almond flour).`;
}

function buildTripEquipmentSavingsTotalSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isTripEquipmentSavingsTotalQuery(normalizedQuery)) return "";

  const checkpoints = extractTripEquipmentSavingsCheckpoints(items);
  const goalieMask = selectTripEquipmentSavingsCheckpoint(checkpoints, "goalie_mask");
  const trip = selectTripEquipmentSavingsCheckpoint(checkpoints, "trip");
  const savings = selectTripEquipmentSavingsCheckpoint(checkpoints, "equipment_savings");
  if (!goalieMask || !trip || !savings) return "";

  const total = goalieMask.amount + trip.amount + savings.amount;
  return `Computed trip and equipment spending total: totaling ${formatPlainNumber(total)} dollars ($${formatNumber(goalieMask.amount)} goalie mask + $${formatNumber(trip.amount)} trip + $${formatNumber(savings.amount)} future equipment savings).`;
}

function buildPurchaseBudgetSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isPurchaseBudgetQuery(normalizedQuery)) return "";

  const candidates = extractPurchaseBudgetCheckpoints(items)
    .filter((checkpoint) => isPurchaseBudgetEvidenceText(checkpoint.normalizedContent))
    .sort((left, right) => {
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.amount - right.amount;
    });
  if (candidates.length === 0) return "";

  const latest = candidates.reduce((best, checkpoint) => {
    if (checkpoint.isUpdate !== best.isUpdate) {
      return checkpoint.isUpdate ? checkpoint : best;
    }
    if (checkpoint.turn !== best.turn) {
      return checkpoint.turn > best.turn ? checkpoint : best;
    }
    return checkpoint.amount > best.amount ? checkpoint : best;
  }, candidates[0]);
  if (!latest) return "";

  return `Most recent purchase budget evidence: $${formatNumber(latest.amount)} budget ceiling.`;
}

interface PurchaseBudgetCheckpoint {
  amount: number;
  turn: number;
  isUpdate: boolean;
  normalizedContent: string;
}

function extractPurchaseBudgetCheckpoints(
  items: readonly EvidencePackItem[],
): PurchaseBudgetCheckpoint[] {
  const checkpoints: PurchaseBudgetCheckpoint[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const segment of extractPurchaseBudgetEvidenceSegments(item.content)) {
      for (const match of segment.text.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
        const amount = parseNumericAmount(match[1]);
        if (amount === undefined) continue;
        const matchIndex = match.index ?? 0;
        if (!isPurchaseBudgetAmountAt(segment.normalized, matchIndex)) continue;
        checkpoints.push({
          amount,
          turn,
          isUpdate: /\b(?:adjusted|increased|updated|raised|new budget|now|recently|current|currently)\b/.test(
            segment.normalized,
          ),
          normalizedContent: segment.normalized,
        });
      }
    }
  }
  return checkpoints;
}

function isPurchaseBudgetQuery(normalizedQuery: string): boolean {
  return /\b(?:budget|ceiling|cap|limit)\b/.test(normalizedQuery) &&
    /\b(?:purchas(?:e|ing)|buy(?:ing)?|new|phone|smartphone|device|laptop|camera|battery|photography|gaming)\b/.test(
      normalizedQuery,
    );
}

function isTutoringGoalDurationQuery(normalizedQuery: string): boolean {
  return /\b(?:how many|weeks?|days?|how long|duration)\b/.test(normalizedQuery) &&
    /\b(?:tutoring|sessions?)\b/.test(normalizedQuery) &&
    /\b(?:math score|80%|80 percent|goal|june\s+1)\b/.test(normalizedQuery);
}

function isTutoringGoalDurationEvidenceText(normalizedContent: string): boolean {
  return hasTutoringGoalDurationFact(normalizedContent) &&
    /\b(?:tutoring|sessions?|math scores?|80%|80\s+percent|march\s+20|june\s+1)\b/.test(
      normalizedContent,
    );
}

function isSalaryQuery(normalizedQuery: string): boolean {
  return /\b(?:salary|annual salary|annually|compensation|earn)\b/.test(normalizedQuery) &&
    /\b(?:senior engineer|job|saint pierre manufacturing ltd|manufacturing|current|annual)\b/.test(
      normalizedQuery,
    );
}

function isSalaryEvidenceText(normalizedContent: string): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  return /\b(?:salary|earn|raise|compensation|senior engineer|saint pierre manufacturing ltd|manufacturing)\b/.test(
    normalizedContent,
  ) && /\bcad\b|\bannually\b|\bannual\b/.test(normalizedContent);
}

function isTrainingCostQuery(normalizedQuery: string): boolean {
  return /\b(?:cost|price|fee|spend|spending|paid|pay|how much|what was)\b/.test(normalizedQuery) &&
    /\b(?:training|course|program|enrolled|enrollment|coursera)\b/.test(normalizedQuery);
}

function isTrainingCostEvidenceText(normalizedContent: string): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  if (!/\b(?:training|course|coursera|program)\b/.test(normalizedContent)) return false;
  if (!/\b(?:cost|costing|spending|investment|afford|fee|price|paid|pay)\b/.test(normalizedContent)) {
    return false;
  }
  if (/\b(?:typically range|range from|sample budget|budget allocation|estimate costs|research costs)\b/.test(
    normalizedContent,
  )) {
    return false;
  }
  return /\b(?:i'm|i am|i've|i have|my|this course|this training|enrolled|enrolling|starting|start date|overview)\b/.test(
    normalizedContent,
  );
}

function isPortfolioFeeTotalQuery(normalizedQuery: string): boolean {
  return /\b(?:how much|total|sum|combined)\b/.test(normalizedQuery) &&
    /\b(?:fees?|costs?|paid)\b/.test(normalizedQuery) &&
    /\b(?:rebalancing|art fund acquisition|wealthfront subscription|wealthfront|portfolio costs?)\b/.test(
      normalizedQuery,
    );
}

function isPortfolioFeeEvidenceText(
  normalizedContent: string,
  normalizedQuery: string,
): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  if (!/\b(?:fee|fees|transaction fees?|subscription fees?|paid)\b/.test(normalizedContent)) {
    return false;
  }
  const wantsRebalancing = /\brebalanc/.test(normalizedQuery);
  const wantsArtFund = /\bart fund/.test(normalizedQuery);
  const wantsWealthfront = /\bwealthfront\b/.test(normalizedQuery);

  return wantsRebalancing && /\brebalanc/.test(normalizedContent) ||
    wantsArtFund && /\bart fund acquisition\b|\bart funds?\b/.test(normalizedContent) ||
    wantsWealthfront && /\bwealthfront\b/.test(normalizedContent);
}

function isRetirementContributionQuery(normalizedQuery: string): boolean {
  return /\b(?:how much|what|amount|monthly|current|contribute|contribution|contributions)\b/.test(
    normalizedQuery,
  ) &&
    /\b(?:roth ira|ira|retirement savings|retirement)\b/.test(normalizedQuery) &&
    /\b(?:contribute|contribution|contributions|monthly)\b/.test(normalizedQuery);
}

function isRetirementContributionEvidenceText(normalizedContent: string): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  return /\broth ira\b/.test(normalizedContent) &&
    /\b(?:contribute|contribution|contributions|contributing)\b/.test(normalizedContent) &&
    /\bmonthly\b/.test(normalizedContent);
}

function isConfidenceBoostTotalQuery(normalizedQuery: string): boolean {
  return /\b(?:confidence boost|boost.*confidence|confidence.*boost|confidence)\b/.test(normalizedQuery) &&
    /\b(?:total|how much|sum|combined)\b/.test(normalizedQuery) &&
    /\b(?:co-leading|co-hosting|co-leading or co-hosting|support group|writing circle|activities)\b/.test(
      normalizedQuery,
    );
}

function isConfidenceBoostEvidenceText(
  normalizedContent: string,
  normalizedQuery: string,
): boolean {
  if (!/\b\d[\d,]*(?:\.\d+)?\s?%/.test(normalizedContent)) return false;
  if (/\bpatricia\b/.test(normalizedQuery) && !/\bpatricia\b/.test(normalizedContent)) {
    return false;
  }
  return /\b(?:co-leading|co-hosting|support group|writing circle)\b/.test(normalizedContent) &&
    /\b(?:confidence|leadership skills?)\b/.test(normalizedContent) &&
    /\b(?:boost(?:ed)?|boost|improv(?:e|ed|ing)|increas(?:e|ed|ing))\b/.test(normalizedContent);
}

function isCacheTtlQuery(normalizedQuery: string): boolean {
  return /\b(?:ttl|time-to-live|time to live|cache configuration|cache config)\b/.test(normalizedQuery) &&
    /\b(?:redis|cache|caching|cached|diffusion features?|api response times?|performance optimizations?)\b/.test(
      normalizedQuery,
    ) &&
    /\b(?:what|setting|seconds?|how long|current|latest|updated|extended)\b/.test(normalizedQuery);
}

function isCacheTtlEvidenceText(normalizedContent: string): boolean {
  return /\b(?:ttl|time-to-live|time to live|cache configuration|cache config)\b/.test(normalizedContent) &&
    /\b(?:redis|cache|caching|cached|diffusion features?|api response times?|performance optimizations?)\b/.test(
      normalizedContent,
    ) &&
    /\b\d[\d,]*(?:\.\d+)?\s?seconds?\b/.test(normalizedContent);
}

function isGameCachingAuthenticationToolCountQuery(normalizedQuery: string): boolean {
  return /\bhow many\b/.test(normalizedQuery) &&
    /\b(?:technologies|technology|tools?)\b/.test(normalizedQuery) &&
    /\bgame\b/.test(normalizedQuery) &&
    /\b(?:cache|caching|cached)\b/.test(normalizedQuery) &&
    /\b(?:authentication|auth)\b/.test(normalizedQuery);
}

function isGameCachingAuthenticationToolCountEvidenceText(normalizedContent: string): boolean {
  const hasToolCue = /\b(?:redis\s*6\.2|redis|phaser\s*3\.55|phaser|node\.?js|express(?:\.js)?|jwt|jsonwebtoken|docker)\b/.test(
    normalizedContent,
  );
  if (!hasToolCue) return false;

  const hasGameCacheCue = /\b(?:game state snapshots?|game caching|phaser|2d rendering|caching layer|cache user|redis store|session caching)\b/.test(
    normalizedContent,
  );
  const hasAuthenticationCue = /\b(?:authentication|auth|jwt|jsonwebtoken|refresh tokens?|token rotation|revoked tokens?|session management)\b/.test(
    normalizedContent,
  );
  const hasRedisSetupCue = /\bredis\b/.test(normalizedContent) &&
    /\b(?:docker|node\.?js|express(?:\.js)?|jwt|jsonwebtoken|phaser\s*3\.55|game state snapshots?)\b/.test(
      normalizedContent,
    );

  return hasGameCacheCue || hasAuthenticationCue || hasRedisSetupCue;
}

function isGeometryTypeCountQuery(normalizedQuery: string): boolean {
  return /\bhow many\b/.test(normalizedQuery) &&
    /\b(?:different\s+)?types?\s+of\s+geometr(?:y|ies)\b/.test(normalizedQuery) &&
    /\b(?:parallel lines?|triangle angle sums?|angle sums?|euclidean|hyperbolic|spherical)\b/.test(
      normalizedQuery,
    );
}

function isGeometryTypeCountEvidenceText(normalizedContent: string): boolean {
  const hasGeometryTypes = /\beuclidean\b/.test(normalizedContent) &&
    /\bhyperbolic\b/.test(normalizedContent) &&
    /\bspherical\b/.test(normalizedContent);
  if (!hasGeometryTypes) return false;
  return /\b(?:geometr(?:y|ies)|parallel lines?|triangle angle sums?|angle sums?|triangles?)\b/.test(
    normalizedContent,
  );
}

function isBitcoinInvestmentPlatformQuery(normalizedQuery: string): boolean {
  return /\bbitcoin\b/.test(normalizedQuery) &&
    /\b(?:invested|investment|how much|amount|platform|exchange)\b/.test(normalizedQuery) &&
    /\b(?:platform|where|which|on)\b/.test(normalizedQuery);
}

function isBitcoinInvestmentPlatformEvidenceText(normalizedContent: string): boolean {
  return /\bbitcoin\b/.test(normalizedContent) &&
    /\bbinance\b/.test(normalizedContent) &&
    (/\$\s?500\b/.test(normalizedContent) || /\b500\s+dollars?\b/.test(normalizedContent));
}

function isCryptoFeeTotalQuery(normalizedQuery: string): boolean {
  return /\b(?:how much|total|combined|sum)\b/.test(normalizedQuery) &&
    /\b(?:fees?|transaction fees?|gas fees?)\b/.test(normalizedQuery) &&
    /\bethereum\b/.test(normalizedQuery) &&
    /\b(?:wallet transfer|transfer|nft)\b/.test(normalizedQuery);
}

function isCryptoFeeEvidenceText(normalizedContent: string): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  const hasEthereumPurchaseWalletFee =
    /\bethereum\b/.test(normalizedContent) &&
    /\bpurchase\b/.test(normalizedContent) &&
    /\bwallet transfer\b/.test(normalizedContent) &&
    (/\$\s?5\b/.test(normalizedContent) || /\b5\s+dollars?\b/.test(normalizedContent));
  const hasRelatedEthereumTransferFee =
    (/\$\s?2\.50\b/.test(normalizedContent) || /\b2\.50\s+dollars?\b/.test(normalizedContent)) &&
    /\b(?:ethereum|staking|deposit|transfer|wallet|crypto)\b/.test(normalizedContent) &&
    /\bfees?\b/.test(normalizedContent);
  const hasNftGasFee =
    /\bnft\b/.test(normalizedContent) &&
    /\bgas fees?\b/.test(normalizedContent) &&
    (/\$\s?10\b/.test(normalizedContent) || /\b10\s+dollars?\b/.test(normalizedContent));
  return hasEthereumPurchaseWalletFee || hasRelatedEthereumTransferFee || hasNftGasFee;
}

function isPopulationGrowthRateQuery(normalizedQuery: string): boolean {
  return /\b(?:what|which|value|using|used|say|said|mention(?:ed)?)\b/.test(normalizedQuery) &&
    /\bgrowth rate\b/.test(normalizedQuery) &&
    /\b(?:population model|population growth model|exponential growth|logistic growth|growth model)\b/.test(
      normalizedQuery,
    );
}

function isPopulationGrowthRateEvidenceText(normalizedContent: string): boolean {
  if (!hasPopulationGrowthRateFact(normalizedContent)) return false;
  return /\b(?:population growth model|population model|exponential growth model|logistic growth model|growth model)\b/.test(
    normalizedContent,
  ) || /\bpopulation growth\b/.test(normalizedContent) &&
    /\b(?:growth rate|k\s*=|r\s*=)\b/.test(normalizedContent);
}

function isAddressQuery(normalizedQuery: string): boolean {
  return /\b(?:address|where\s+(?:do\s+)?i\s+live|where\s+my\s+place\s+is|my place|home address)\b/.test(
    normalizedQuery,
  ) &&
    /\b(?:what|where|mention(?:ed)?|address|live)\b/.test(normalizedQuery);
}

function isAddressEvidenceText(content: string, normalizedQuery: string): boolean {
  const normalizedContent = content.toLowerCase();
  if (!hasStreetAddress(content)) return false;
  if (/\bwhere\s+(?:do\s+)?i\s+live\b/.test(normalizedQuery)) {
    return /\b(?:my place|home|apartment|where i live|i live|address)\b/.test(
      normalizedContent,
    );
  }
  return /\b(?:address|my place|home|apartment|location|near|street)\b/.test(
    normalizedContent,
  );
}

function hasStreetAddress(content: string): boolean {
  return extractStreetAddresses(content).length > 0;
}

function extractStreetAddresses(content: string): string[] {
  const addresses = new Set<string>();
  for (const match of content.matchAll(STREET_ADDRESS_PATTERN)) {
    const address = match[0]
      .replace(/[,.!?;:]+$/g, "")
      .replace(/\s+/g, " ")
      .trim();
    if (address) addresses.add(address);
  }
  return [...addresses];
}

function scoreAddressEvidenceSpecificity(
  normalizedContent: string,
  normalizedQuery: string,
): number {
  let score = 0;
  if (/\b(?:my place|home|apartment|where i live|i live)\b/.test(normalizedContent)) {
    score += 24;
  }
  if (/\baddress\b/.test(normalizedContent)) score += 16;
  if (/\bnear\b/.test(normalizedContent)) score += 6;
  score += countFocusedTermOverlap(normalizedContent, normalizedQuery);
  return score;
}

function isBakingPurchaseTotalQuery(normalizedQuery: string): boolean {
  return /\b(?:how much|total|combined|sum|spent|spending)\b/.test(normalizedQuery) &&
    /\b(?:kitchenaid|stand mixer|mixer)\b/.test(normalizedQuery) &&
    /\b(?:organic almond flour|almond flour)\b/.test(normalizedQuery);
}

function isBakingPurchaseTotalEvidenceText(normalizedContent: string): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  return /\b(?:kitchenaid|stand mixer|mixer|organic almond flour|almond flour)\b/.test(
    normalizedContent,
  ) &&
    /\b(?:spent|spending|cost|worth|investment|paid|bought|organic)\b/.test(
      normalizedContent,
    );
}

function isTripEquipmentSavingsTotalQuery(normalizedQuery: string): boolean {
  return /\b(?:how much|total|combined|sum|spend|spent|spending|money)\b/.test(
    normalizedQuery,
  ) &&
    /\btrip\b/.test(normalizedQuery) &&
    /\b(?:future equipment|equipment savings?|equipment|savings?|saving|save|goalie mask|mask)\b/.test(
      normalizedQuery,
    );
}

function isTripEquipmentSavingsEvidenceText(normalizedContent: string): boolean {
  if (!hasMoneyOrPercent(normalizedContent)) return false;
  if (!/\b(?:goalie mask|trip|future equipment|equipment needs?|savings?|set aside|delayed summer trip|courtney)\b/.test(
    normalizedContent,
  )) {
    return false;
  }
  return /\b(?:spent|spending|expense|set aside|budget|savings?|save|split|resources|prepared|future equipment|trip)\b/.test(
    normalizedContent,
  );
}

function isMvpDeadlineRemainingQuery(normalizedQuery: string): boolean {
  return /\b(?:mvp|minimum viable product)\b/.test(normalizedQuery) &&
    /\bdeadline\b/.test(normalizedQuery) &&
    /\b(?:days?|left|remaining|development period|coding|begin coding|start)\b/.test(
      normalizedQuery,
    );
}

function isMvpDeadlineEvidenceText(normalizedContent: string): boolean {
  return /\b(?:mvp|development period|started coding|start(?:ed)? development|begin coding|coding)\b/.test(
    normalizedContent,
  ) &&
    /\b(?:may\s+1(?:,\s*2024)?|june\s+12(?:,\s*2024)?|6 weeks?|six weeks?|development deadline)\b/.test(
      normalizedContent,
    );
}

type BakingPurchaseKind = "kitchenaid_mixer" | "organic_almond_flour";

type TripEquipmentSavingsKind = "goalie_mask" | "trip" | "equipment_savings";

interface BakingPurchaseCheckpoint {
  kind: BakingPurchaseKind;
  amount: number;
  turn: number;
  specificity: number;
}

function extractBakingPurchaseCheckpoints(
  items: readonly EvidencePackItem[],
): BakingPurchaseCheckpoint[] {
  const checkpoints: BakingPurchaseCheckpoint[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const segment of splitEvidenceSegments(item.content)) {
      const normalizedSegment = segment.toLowerCase();
      if (normalizedSegment.startsWith("normalized numeric cues:")) continue;
      const kind = classifyBakingPurchaseKind(normalizedSegment);
      if (!kind) continue;
      if (!isBakingPurchaseTotalEvidenceText(normalizedSegment)) continue;
      const specificity = scoreBakingPurchaseSpecificity(normalizedSegment, kind);
      for (const match of segment.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
        const amount = parseNumericAmount(match[1]);
        if (amount === undefined) continue;
        checkpoints.push({ kind, amount, turn, specificity });
      }
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (left.kind !== right.kind) return left.kind.localeCompare(right.kind);
      if (right.specificity !== left.specificity) return right.specificity - left.specificity;
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.amount - right.amount;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.kind !== all[index - 1]?.kind ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.amount !== all[index - 1]?.amount
    );
}

function classifyBakingPurchaseKind(
  normalizedSegment: string,
): BakingPurchaseKind | undefined {
  if (/\bkitchenaid\b|\bstand mixer\b|\bmixer\b/.test(normalizedSegment)) {
    return "kitchenaid_mixer";
  }
  if (/\borganic almond flour\b|\balmond flour\b/.test(normalizedSegment)) {
    return "organic_almond_flour";
  }
  return undefined;
}

function scoreBakingPurchaseSpecificity(
  normalizedSegment: string,
  kind: BakingPurchaseKind,
): number {
  let score = 0;
  if (kind === "kitchenaid_mixer") {
    if (/\bkitchenaid\b/.test(normalizedSegment)) score += 24;
    if (/\bspent\b|\bspending\b|\bbought\b/.test(normalizedSegment)) score += 16;
  }
  if (kind === "organic_almond_flour") {
    if (/\borganic almond flour\b/.test(normalizedSegment)) score += 24;
    if (/\bspent\b|\bspending\b|\bbought\b/.test(normalizedSegment)) score += 16;
    if (/\bextra\s+\$/.test(normalizedSegment)) score -= 28;
  }
  return score;
}

function selectBakingPurchaseCheckpoint(
  checkpoints: readonly BakingPurchaseCheckpoint[],
  kind: BakingPurchaseKind,
): BakingPurchaseCheckpoint | undefined {
  const candidates = checkpoints.filter((checkpoint) => checkpoint.kind === kind);
  if (candidates.length === 0) return undefined;
  return candidates.reduce((best, checkpoint) => {
    if (checkpoint.specificity !== best.specificity) {
      return checkpoint.specificity > best.specificity ? checkpoint : best;
    }
    if (checkpoint.turn !== best.turn) {
      return checkpoint.turn < best.turn ? checkpoint : best;
    }
    return checkpoint.amount < best.amount ? checkpoint : best;
  }, candidates[0]);
}

interface TripEquipmentSavingsCheckpoint {
  kind: TripEquipmentSavingsKind;
  amount: number;
  turn: number;
  role?: string;
  specificity: number;
}

function extractTripEquipmentSavingsCheckpoints(
  items: readonly EvidencePackItem[],
): TripEquipmentSavingsCheckpoint[] {
  const checkpoints: TripEquipmentSavingsCheckpoint[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const segment of splitEvidenceSegments(item.content)) {
      const normalizedSegment = segment.toLowerCase();
      if (normalizedSegment.startsWith("normalized numeric cues:")) continue;
      if (!isTripEquipmentSavingsEvidenceText(normalizedSegment)) continue;
      for (const match of segment.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
        const amount = parseNumericAmount(match[1]);
        if (amount === undefined) continue;
        const amountIndex = match.index ?? 0;
        const kind = classifyTripEquipmentSavingsKind(normalizedSegment, amountIndex);
        if (!kind) continue;
        checkpoints.push({
          kind,
          amount,
          turn,
          role: item.role,
          specificity: scoreTripEquipmentSavingsAmount(normalizedSegment, amountIndex, kind),
        });
      }
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (left.kind !== right.kind) return left.kind.localeCompare(right.kind);
      if (right.specificity !== left.specificity) return right.specificity - left.specificity;
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.amount - right.amount;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.kind !== all[index - 1]?.kind ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.amount !== all[index - 1]?.amount
    );
}

function classifyTripEquipmentSavingsKind(
  normalizedSegment: string,
  amountIndex: number,
): TripEquipmentSavingsKind | undefined {
  const candidates: Array<{ kind: TripEquipmentSavingsKind; score: number }> = [
    {
      kind: "goalie_mask",
      score: scoreTripEquipmentSavingsAmount(normalizedSegment, amountIndex, "goalie_mask"),
    },
    {
      kind: "trip",
      score: scoreTripEquipmentSavingsAmount(normalizedSegment, amountIndex, "trip"),
    },
    {
      kind: "equipment_savings",
      score: scoreTripEquipmentSavingsAmount(
        normalizedSegment,
        amountIndex,
        "equipment_savings",
      ),
    },
  ];
  const scores = candidates.filter((candidate) => candidate.score > 0);

  return scores.sort((left, right) => {
    if (right.score !== left.score) return right.score - left.score;
    return left.kind.localeCompare(right.kind);
  })[0]?.kind;
}

function scoreTripEquipmentSavingsAmount(
  normalizedSegment: string,
  amountIndex: number,
  kind: TripEquipmentSavingsKind,
): number {
  const cuesByKind: Record<TripEquipmentSavingsKind, RegExp[]> = {
    goalie_mask: [/\bgoalie mask\b/g, /\bmask\b/g],
    trip: [/\btrip\b/g, /\bdelayed summer trip\b/g, /\bcourtney\b/g, /\baugust\s+20\b/g],
    equipment_savings: [
      /\bfuture equipment\b/g,
      /\bequipment needs?\b/g,
      /\bsavings?\b/g,
      /\bsav(?:e|ing)\b/g,
      /\bprepared\b/g,
    ],
  };
  const distance = nearestPatternDistance(normalizedSegment, amountIndex, cuesByKind[kind]);
  if (distance === undefined || distance > 90) return 0;

  let score = 120 - distance;
  const amountWindow = normalizedSegment.slice(
    Math.max(0, amountIndex - 50),
    Math.min(normalizedSegment.length, amountIndex + 90),
  );

  if (kind === "goalie_mask" && /\bspent\b|\bexpense\b/.test(amountWindow)) {
    score += 30;
  }
  if (kind === "trip" && /\b(?:for the trip|trip budget|budget under|delayed summer trip)\b/.test(amountWindow)) {
    score += 30;
  }
  if (kind === "equipment_savings" && /\b(?:for savings|future equipment|equipment needs?)\b/.test(amountWindow)) {
    score += 30;
  }
  if (/\b(?:total trip budget|need additional funds|adjust the budget)\b/.test(normalizedSegment)) {
    score -= 80;
  }
  return Math.max(0, score);
}

function nearestPatternDistance(
  text: string,
  amountIndex: number,
  patterns: readonly RegExp[],
): number | undefined {
  let nearest: number | undefined;
  for (const pattern of patterns) {
    pattern.lastIndex = 0;
    for (const match of text.matchAll(pattern)) {
      const index = match.index ?? 0;
      const distance = Math.abs(index - amountIndex);
      if (nearest === undefined || distance < nearest) {
        nearest = distance;
      }
    }
  }
  return nearest;
}

function selectTripEquipmentSavingsCheckpoint(
  checkpoints: readonly TripEquipmentSavingsCheckpoint[],
  kind: TripEquipmentSavingsKind,
): TripEquipmentSavingsCheckpoint | undefined {
  const candidates = checkpoints.filter((checkpoint) => checkpoint.kind === kind);
  if (candidates.length === 0) return undefined;

  const userCandidates = candidates.filter((checkpoint) => checkpoint.role === "user");
  const pool = userCandidates.length > 0 ? userCandidates : candidates;
  return pool.reduce((best, checkpoint) => {
    if (checkpoint.specificity !== best.specificity) {
      return checkpoint.specificity > best.specificity ? checkpoint : best;
    }
    if (checkpoint.turn !== best.turn) {
      return checkpoint.turn > best.turn ? checkpoint : best;
    }
    return checkpoint.amount < best.amount ? checkpoint : best;
  }, pool[0]);
}

function isPurchaseBudgetEvidenceText(normalizedContent: string): boolean {
  return extractPurchaseBudgetEvidenceSegments(normalizedContent).length > 0;
}

interface PurchaseBudgetEvidenceSegment {
  text: string;
  normalized: string;
}

function extractPurchaseBudgetEvidenceSegments(content: string): PurchaseBudgetEvidenceSegment[] {
  return splitEvidenceSegments(content)
    .map((text) => ({ text, normalized: text.toLowerCase() }))
    .filter((segment) => isPurchaseBudgetEvidenceSegment(segment.normalized));
}

function splitEvidenceSegments(content: string): string[] {
  const segments: string[] = [];
  for (const part of content.split(/\r?\n+/)) {
    const trimmed = part.trim();
    if (!trimmed) continue;
    const sentenceMatches = trimmed.match(/[^.!?]+[.!?]?/g);
    if (!sentenceMatches) {
      segments.push(trimmed);
      continue;
    }
    for (const sentence of sentenceMatches) {
      const normalized = sentence.trim();
      if (normalized) segments.push(normalized);
    }
  }
  return segments;
}

function isPurchaseBudgetEvidenceSegment(normalizedSegment: string): boolean {
  if (!hasMoneyOrPercent(normalizedSegment)) return false;
  if (!/\b(?:budget|ceiling|cap|limit)\b/.test(normalizedSegment)) return false;
  if (isAccessoryBudgetOnlyText(normalizedSegment)) return false;

  const hasDirectDevicePurchase =
    /\b(?:new\s+)?(?:phone|smartphone|device|laptop)\b/.test(normalizedSegment) ||
    /\b(?:buy(?:ing)?|purchas(?:e|ing)|get(?:ting)?)\b[^.?\n]{0,80}\b(?:phone|smartphone|device|laptop)\b/.test(
      normalizedSegment,
    );
  if (!hasDirectDevicePurchase) return false;

  return /\b(?:camera|battery|photography|gaming|ceiling|cap|limit|purchase|buy|new)\b/.test(
    normalizedSegment,
  );
}

function isAccessoryBudgetOnlyText(normalizedContent: string): boolean {
  const hasAccessoryBudgetCue = /\b(?:accessor(?:y|ies)|apps?|subscriptions?|headsets?|screen protectors?|cases?|chargers?|charging stands?|controllers?|cloud storage|vacation)\b/.test(
    normalizedContent,
  );
  if (!hasAccessoryBudgetCue) return false;

  return !/\b(?:new phone|new smartphone|new device|new laptop|phone purchase|smartphone purchase|device purchase|laptop purchase|buy(?:ing)? a new (?:phone|smartphone|device|laptop)|purchas(?:e|ing) a new (?:phone|smartphone|device|laptop))\b/.test(
    normalizedContent,
  );
}

function isPurchaseBudgetAmountAt(normalizedContent: string, amountIndex: number): boolean {
  const windowStart = Math.max(0, amountIndex - 80);
  const windowEnd = Math.min(normalizedContent.length, amountIndex + 120);
  const nearAmount = normalizedContent.slice(windowStart, windowEnd);
  return /\b(?:budget|ceiling|cap|limit)\b/.test(nearAmount);
}

function buildEmergencyFundDurationSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (
    !/\bemergency fund\b/.test(normalizedQuery) ||
    !/\b(?:how long|after|between|duration|days?|time)\b/.test(normalizedQuery)
  ) {
    return "";
  }

  const checkpoints = items
    .flatMap((item) => extractEmergencyFundCheckpoints(item.content))
    .sort((left, right) => compareEmergencyFundCheckpoints(left, right));
  if (checkpoints.length < 2) {
    return "";
  }

  const start = checkpoints.find((checkpoint) => !checkpoint.isFullGoal) ??
    checkpoints[0];
  if (!start) {
    return "";
  }
  const end = checkpoints
    .filter((checkpoint) => checkpoint !== start)
    .find((checkpoint) => checkpoint.isFullGoal && checkpoint.amount >= start.amount) ??
    checkpoints.find((checkpoint) => checkpoint.amount > start.amount);
  if (!end) {
    return "";
  }

  const days = daysBetweenMonthDays(start.date, end.date);
  if (days <= 0) {
    return "";
  }
  return `Computed temporal interval: ${days} days from ${formatMonthDay(start.date)} till ${formatMonthDay(end.date)}.`;
}

interface MonthDay {
  month: number;
  day: number;
  year?: number;
}

interface EmergencyFundCheckpoint {
  amount: number;
  date: MonthDay;
  isFullGoal: boolean;
}

function extractEmergencyFundCheckpoints(content: string): EmergencyFundCheckpoint[] {
  if (!/\bemergency fund\b/i.test(content)) {
    return [];
  }
  const checkpoints: EmergencyFundCheckpoint[] = [];
  const reachedAmountByDate =
    /\breached\s+\$?\s?(\d[\d,]*(?:\.\d+)?)[^.?\n]{0,180}?\b(?:by|on)\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})(?:,\s*(\d{4}))?/gi;
  for (const match of content.matchAll(reachedAmountByDate)) {
    const amount = parseNumericAmount(match[1]);
    const date = parseMonthDay(match[2], match[3], match[4]);
    if (amount === undefined || !date) continue;
    checkpoints.push({
      amount,
      date,
      isFullGoal: false,
    });
  }

  const reachedGoalByDate =
    /\breached(?:\s+my)?\s+emergency fund goal(?:\s+of)?\s+\$?\s?(\d[\d,]*(?:\.\d+)?)[^.?\n]{0,120}?\b(?:by|on)\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})(?:,\s*(\d{4}))?/gi;
  for (const match of content.matchAll(reachedGoalByDate)) {
    const amount = parseNumericAmount(match[1]);
    const date = parseMonthDay(match[2], match[3], match[4]);
    if (amount === undefined || !date) continue;
    checkpoints.push({
      amount,
      date,
      isFullGoal: true,
    });
  }

  return dedupeCheckpoints(checkpoints);
}

function dedupeCheckpoints(
  checkpoints: readonly EmergencyFundCheckpoint[],
): EmergencyFundCheckpoint[] {
  const seen = new Set<string>();
  const deduped: EmergencyFundCheckpoint[] = [];
  for (const checkpoint of checkpoints) {
    const key = `${checkpoint.amount}:${checkpoint.date.year ?? ""}:${checkpoint.date.month}:${checkpoint.date.day}:${checkpoint.isFullGoal}`;
    if (seen.has(key)) continue;
    seen.add(key);
    deduped.push(checkpoint);
  }
  return deduped;
}

function parseNumericAmount(value: string | undefined): number | undefined {
  if (!value) return undefined;
  const parsed = Number(value.replace(/,/g, ""));
  return Number.isFinite(parsed) ? parsed : undefined;
}

function parseMonthDay(
  monthName: string | undefined,
  dayValue: string | undefined,
  yearValue?: string,
): MonthDay | undefined {
  if (!monthName || !dayValue) return undefined;
  const month = MONTH_INDEX_BY_NAME[monthName.toLowerCase()];
  const day = Number(dayValue);
  if (!month || !Number.isInteger(day) || day < 1 || day > 31) {
    return undefined;
  }
  const year = yearValue === undefined ? undefined : Number(yearValue);
  if (year !== undefined && (!Number.isInteger(year) || year < 0)) {
    return undefined;
  }
  return year === undefined ? { month, day } : { month, day, year };
}

function daysBetweenMonthDays(start: MonthDay, end: MonthDay): number {
  const startYear = start.year ?? 2024;
  const endYear = end.year ?? startYear;
  const startTime = Date.UTC(startYear, start.month - 1, start.day);
  let endTime = Date.UTC(endYear, end.month - 1, end.day);
  if (end.year === undefined && endTime < startTime) {
    endTime = Date.UTC(startYear + 1, end.month - 1, end.day);
  }
  return Math.round((endTime - startTime) / 86_400_000);
}

function formatMonthDay(date: MonthDay): string {
  const month = MONTH_NAME_BY_INDEX[date.month] ?? "Unknown";
  return date.year === undefined ? `${month} ${date.day}` : `${month} ${date.day}, ${date.year}`;
}

function compareEmergencyFundCheckpoints(
  left: EmergencyFundCheckpoint,
  right: EmergencyFundCheckpoint,
): number {
  const leftYear = left.date.year ?? 2024;
  const rightYear = right.date.year ?? 2024;
  if (leftYear !== rightYear) return leftYear - rightYear;
  if (left.date.month !== right.date.month) return left.date.month - right.date.month;
  if (left.date.day !== right.date.day) return left.date.day - right.date.day;
  return left.amount - right.amount;
}

function buildWordCountDeltaSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!/\bword count\b/.test(normalizedQuery) || !/\b(?:increase|increased|from|until)\b/.test(normalizedQuery)) {
    return "";
  }
  const joined = items.map((item) => item.content).join("\n");
  const match = joined.match(
    /\b(?:increased|increase|adjusted)[^.?\n]{0,120}?\bfrom\s+(\d[\d,]*)\s+to\s+(\d[\d,]*)\s+words\b/i,
  ) ?? joined.match(
    /\bfrom\s+(\d[\d,]*)\s+to\s+(\d[\d,]*)\s+words\b/i,
  );
  if (!match?.[1] || !match[2]) {
    return "";
  }
  const start = parseNumericAmount(match[1]);
  const end = parseNumericAmount(match[2]);
  if (start === undefined || end === undefined || end <= start) {
    return "";
  }
  const delta = end - start;
  return `Computed word-count increase: ${delta} words, from ${formatNumber(start)} to ${formatNumber(end)} words.`;
}

function buildEditingChallengeDurationSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!/\bediting challenge\b/.test(normalizedQuery) || !/\b(?:how many|days?|between|passed)\b/.test(normalizedQuery)) {
    return "";
  }
  const joined = items.map((item) => item.content).join("\n");
  const startMatch = joined.match(
    /\b30-day editing challenge\s+starting\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})/i,
  );
  const endMatch = joined.match(
    /\b15-day clarity editing challenge\s+from\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})\s+to\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})/i,
  );
  const start = parseMonthDay(startMatch?.[1], startMatch?.[2]);
  const end = parseMonthDay(endMatch?.[3], endMatch?.[4]);
  if (!start || !end) {
    return "";
  }
  const days = daysBetweenMonthDays(start, end);
  if (days <= 0) {
    return "";
  }
  return `Computed editing-challenge interval: ${days} days from ${formatMonthDay(start)} till ${formatMonthDay(end)}.`;
}

function buildTutoringGoalDurationSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (
    !/\b(?:weeks?|days?|how many)\b/.test(normalizedQuery) ||
    !/\b(?:tutoring|sessions?)\b/.test(normalizedQuery) ||
    !/\b(?:math score|80%|80 percent|goal)\b/.test(normalizedQuery)
  ) {
    return "";
  }

  const joined = items.map((item) => item.content).join("\n");
  const startMatch = joined.match(
    /\b(?:twice weekly sessions?|tutoring sessions?)[^.?\n]{0,120}?\bstarting\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})\b/i,
  ) ?? joined.match(
    /\bstarting\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})[^.?\n]{0,120}?\b(?:twice weekly sessions?|tutoring sessions?)\b/i,
  );
  const endMatch = joined.match(
    /\b(?:80%|80\s+percent|math scores?)[^.?\n]{0,120}?\bby\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})(?:,\s*\d{4})?\b/i,
  );
  const start = parseMonthDay(startMatch?.[1], startMatch?.[2]);
  const end = parseMonthDay(endMatch?.[1], endMatch?.[2]);
  if (!start || !end) return "";

  const days = daysBetweenMonthDays(start, end);
  if (days <= 0) return "";
  const approximateWeeks = Math.ceil(days / 7);
  return `Computed tutoring interval: approximately ${formatNumber(approximateWeeks)} weeks from ${formatMonthDay(start)} till ${formatMonthDay(end)}.`;
}

function buildTrainingCostSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isTrainingCostQuery(normalizedQuery)) return "";

  const checkpoints = extractTrainingCostCheckpoints(items);
  if (checkpoints.length === 0) return "";

  const selected = checkpoints.reduce((best, checkpoint) => {
    if (checkpoint.specificity !== best.specificity) {
      return checkpoint.specificity > best.specificity ? checkpoint : best;
    }
    if (checkpoint.turn !== best.turn) {
      return checkpoint.turn < best.turn ? checkpoint : best;
    }
    return checkpoint.amount < best.amount ? checkpoint : best;
  }, checkpoints[0]);
  if (!selected) return "";

  return `Training cost evidence: $${formatNumber(selected.amount)}.`;
}

interface TrainingCostCheckpoint {
  amount: number;
  turn: number;
  specificity: number;
}

function extractTrainingCostCheckpoints(
  items: readonly EvidencePackItem[],
): TrainingCostCheckpoint[] {
  const checkpoints: TrainingCostCheckpoint[] = [];
  for (const item of items) {
    const normalizedContent = item.content.toLowerCase();
    if (!isTrainingCostEvidenceText(normalizedContent)) continue;
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    const specificity = scoreTrainingCostSpecificity(normalizedContent);
    for (const match of item.content.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
      const amount = parseNumericAmount(match[1]);
      if (amount === undefined) continue;
      checkpoints.push({ amount, turn, specificity });
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (right.specificity !== left.specificity) return right.specificity - left.specificity;
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.amount - right.amount;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.amount !== all[index - 1]?.amount
    );
}

function scoreTrainingCostSpecificity(normalizedContent: string): number {
  let score = 0;
  if (/\btraining\b/.test(normalizedContent)) score += 12;
  if (/\b(?:leadership training|12-week|coursera|starting february 5|february 5)\b/.test(normalizedContent)) {
    score += 12;
  }
  if (/\b(?:costing|cost:|spending)\b/.test(normalizedContent)) score += 8;
  if (/\b(?:enrolled|enrolling|this course|this training)\b/.test(normalizedContent)) score += 6;
  if (/\bstrategic thinking course\b/.test(normalizedContent)) score -= 6;
  return score;
}

function buildPortfolioFeeTotalSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isPortfolioFeeTotalQuery(normalizedQuery)) return "";

  const checkpoints = extractPortfolioFeeCheckpoints(items, normalizedQuery);
  if (checkpoints.length === 0) return "";

  const earliestByKind = new Map<PortfolioFeeKind, PortfolioFeeCheckpoint>();
  for (const checkpoint of checkpoints) {
    const existing = earliestByKind.get(checkpoint.kind);
    if (!existing || checkpoint.turn < existing.turn) {
      earliestByKind.set(checkpoint.kind, checkpoint);
    }
  }
  const selected = [...earliestByKind.values()].sort((left, right) => left.turn - right.turn);
  const total = selected.reduce((sum, checkpoint) => sum + checkpoint.amount, 0);
  if (total <= 0) return "";

  const parts = selected
    .map((checkpoint) => `$${formatNumber(checkpoint.amount)} ${PORTFOLIO_FEE_LABELS[checkpoint.kind]}`)
    .join(" + ");
  return `Computed portfolio fee total: $${formatNumber(total)} total (${parts}).`;
}

type PortfolioFeeKind = "rebalancing" | "art_fund" | "wealthfront";

interface PortfolioFeeCheckpoint {
  kind: PortfolioFeeKind;
  amount: number;
  turn: number;
}

const PORTFOLIO_FEE_LABELS: Record<PortfolioFeeKind, string> = {
  rebalancing: "rebalancing fees",
  art_fund: "art fund acquisition fees",
  wealthfront: "Wealthfront subscription fees",
};

function extractPortfolioFeeCheckpoints(
  items: readonly EvidencePackItem[],
  normalizedQuery: string,
): PortfolioFeeCheckpoint[] {
  const checkpoints: PortfolioFeeCheckpoint[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const segment of splitEvidenceSegments(item.content)) {
      const normalizedSegment = segment.toLowerCase();
      if (normalizedSegment.startsWith("normalized numeric cues:")) continue;
      const kind = classifyPortfolioFeeKind(normalizedSegment, normalizedQuery);
      if (!kind) continue;
      if (!/\b(?:fee|fees|paid|costs?)\b/.test(normalizedSegment)) continue;
      for (const match of segment.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
        const amount = parseNumericAmount(match[1]);
        if (amount === undefined) continue;
        checkpoints.push({ kind, amount, turn });
      }
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (left.turn !== right.turn) return left.turn - right.turn;
      if (left.kind !== right.kind) return left.kind.localeCompare(right.kind);
      return left.amount - right.amount;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.kind !== all[index - 1]?.kind ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.amount !== all[index - 1]?.amount
    );
}

function classifyPortfolioFeeKind(
  normalizedSegment: string,
  normalizedQuery: string,
): PortfolioFeeKind | undefined {
  if (/\brebalanc/.test(normalizedQuery) && /\brebalanc/.test(normalizedSegment)) {
    return "rebalancing";
  }
  if (/\bart fund/.test(normalizedQuery) && /\bart fund acquisition\b|\bart funds?\b/.test(normalizedSegment)) {
    return "art_fund";
  }
  if (/\bwealthfront\b/.test(normalizedQuery) && /\bwealthfront\b/.test(normalizedSegment)) {
    return "wealthfront";
  }
  return undefined;
}

function buildRetirementContributionSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isRetirementContributionQuery(normalizedQuery)) return "";

  const checkpoints = extractRetirementContributionCheckpoints(items);
  if (checkpoints.length === 0) return "";

  const latest = checkpoints.reduce((best, checkpoint) => {
    if (checkpoint.isUpdate !== best.isUpdate) {
      return checkpoint.isUpdate ? checkpoint : best;
    }
    if (checkpoint.turn !== best.turn) {
      return checkpoint.turn > best.turn ? checkpoint : best;
    }
    return checkpoint.amount > best.amount ? checkpoint : best;
  }, checkpoints[0]);
  if (!latest) return "";

  return `Most recent Roth IRA contribution evidence: $${formatNumber(latest.amount)} monthly.`;
}

interface RetirementContributionCheckpoint {
  amount: number;
  turn: number;
  isUpdate: boolean;
}

function extractRetirementContributionCheckpoints(
  items: readonly EvidencePackItem[],
): RetirementContributionCheckpoint[] {
  const checkpoints: RetirementContributionCheckpoint[] = [];
  for (const item of items) {
    const normalizedContent = item.content.toLowerCase();
    if (!isRetirementContributionEvidenceText(normalizedContent)) continue;
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const match of item.content.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
      const amount = parseNumericAmount(match[1]);
      if (amount === undefined) continue;
      checkpoints.push({
        amount,
        turn,
        isUpdate: /\b(?:increased|starting|current|currently|now|recently|latest)\b/.test(
          normalizedContent,
        ),
      });
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.amount - right.amount;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.amount !== all[index - 1]?.amount
    );
}

function buildSalaryFactSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isSalaryQuery(normalizedQuery)) return "";

  const checkpoints = extractSalaryCheckpoints(items);
  if (checkpoints.length === 0) return "";

  const wantsEmployer = /\bsaint pierre manufacturing ltd\b/.test(normalizedQuery);
  const candidates = wantsEmployer
    ? checkpoints.filter((checkpoint) => checkpoint.hasEmployer)
    : checkpoints.filter((checkpoint) => !checkpoint.isRaise);
  const pool = candidates.length > 0 ? candidates : checkpoints;
  const selected = pool.reduce((best, checkpoint) => {
    if (wantsEmployer && checkpoint.isRaise !== best.isRaise) {
      return checkpoint.isRaise ? checkpoint : best;
    }
    if (checkpoint.turn !== best.turn) {
      return checkpoint.turn > best.turn ? checkpoint : best;
    }
    return checkpoint.amount > best.amount ? checkpoint : best;
  }, pool[0]);
  if (!selected) return "";

  const employer = selected.hasEmployer
    ? " as senior engineer at Saint Pierre Manufacturing Ltd"
    : "";
  return `Salary evidence: approximately $${formatNumber(selected.amount)} CAD annually${employer}.`;
}

interface SalaryCheckpoint {
  amount: number;
  turn: number;
  hasEmployer: boolean;
  isRaise: boolean;
}

function extractSalaryCheckpoints(
  items: readonly EvidencePackItem[],
): SalaryCheckpoint[] {
  const checkpoints: SalaryCheckpoint[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    const normalizedContent = item.content.toLowerCase();
    if (!isSalaryEvidenceText(normalizedContent)) continue;
    for (const match of item.content.matchAll(/\$\s?(\d[\d,]*(?:\.\d+)?)/g)) {
      const amount = parseNumericAmount(match[1]);
      if (amount === undefined) continue;
      checkpoints.push({
        amount,
        turn,
        hasEmployer: /\bsaint pierre manufacturing ltd\b/.test(normalizedContent),
        isRaise: /\braise\b|\braised\b|\bupdated\b|\bincreased\b|\bnow\b|\bcurrent(?:ly)?\b/.test(
          normalizedContent,
        ),
      });
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.amount - right.amount;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.amount !== all[index - 1]?.amount
    );
}

interface ConfidenceBoostMention {
  percent: number;
  turn: number;
  role?: string;
  normalizedSegment: string;
}

function buildConfidenceBoostTotalSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isConfidenceBoostTotalQuery(normalizedQuery)) return "";

  const mentions = extractConfidenceBoostMentions(items, normalizedQuery);
  if (mentions.length === 0) return "";

  const rawTotal = mentions.reduce((sum, mention) => sum + mention.percent, 0);
  const userMentions = mentions.filter((mention) => mention.role === "user");
  const dedupedUserMentions = dedupeConfidenceBoostMentions(userMentions.length > 0 ? userMentions : mentions);
  const dedupedUserTotal = dedupedUserMentions.reduce((sum, mention) => sum + mention.percent, 0);

  const rawParts = mentions.map((mention) => `${formatNumber(mention.percent)}%`).join(" + ");
  const dedupedParts = dedupedUserMentions.map((mention) => `${formatNumber(mention.percent)}%`).join(" + ");

  return `Computed confidence-boost totals: raw co-leading/co-hosting confidence-boost mentions sum to ${formatNumber(rawTotal)}% (${rawParts}); de-duplicated user-reported boosts sum to ${formatNumber(dedupedUserTotal)}% (${dedupedParts}).`;
}

function buildCacheTtlSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isCacheTtlQuery(normalizedQuery)) return "";

  for (const item of items) {
    const normalized = item.content.toLowerCase();
    if (!isCacheTtlEvidenceText(normalized)) continue;
    const match = item.content.match(/\b(\d[\d,]*(?:\.\d+)?)\s?(seconds?)\b/i);
    if (!match?.[1] || !match[2]) continue;
    const seconds = normalizeNumericCue(match[1]);
    return `Targeted cache TTL fact: Redis cache TTL is ${seconds} ${match[2].toLowerCase()} for diffusion-feature caching and API response-time optimization.`;
  }

  return "";
}

function buildGameCachingAuthenticationToolCountSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isGameCachingAuthenticationToolCountQuery(normalizedQuery)) return "";

  const normalizedEvidence = items.map((item) => item.content.toLowerCase()).join("\n");
  const matchedTools = [
    { label: "Redis 6.2", pattern: /\bredis\s*6\.2\b/ },
    { label: "Phaser 3.55", pattern: /\bphaser\s*3\.55\b/ },
    { label: "Node.js", pattern: /\bnode\.?js\b/ },
    { label: "Express.js", pattern: /\bexpress\.?js\b/ },
    { label: "JWT", pattern: /\b(?:jwt|jsonwebtoken)\b/ },
    { label: "Docker", pattern: /\bdocker\b/ },
  ]
    .filter((tool) => tool.pattern.test(normalizedEvidence))
    .map((tool) => tool.label);
  if (matchedTools.length === 0) return "";

  return `Computed game caching/authentication tool count: ${formatNumber(matchedTools.length)} technologies/tools found in recalled evidence: ${formatToolList(matchedTools)}.`;
}

function formatToolList(tools: readonly string[]): string {
  if (tools.length <= 1) return tools[0] ?? "";
  if (tools.length === 2) return `${tools[0]} and ${tools[1]}`;
  return `${tools.slice(0, -1).join(", ")}, and ${tools[tools.length - 1]}`;
}

function buildGeometryTypeCountSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isGeometryTypeCountQuery(normalizedQuery)) return "";

  const normalizedEvidence = items.map((item) => item.content.toLowerCase()).join("\n");
  if (!isGeometryTypeCountEvidenceText(normalizedEvidence)) return "";

  return "Computed geometry type count: Three types: Euclidean, hyperbolic, and spherical geometries.";
}

function buildBitcoinInvestmentPlatformSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isBitcoinInvestmentPlatformQuery(normalizedQuery)) return "";

  const normalizedEvidence = items.map((item) => item.content.toLowerCase()).join("\n");
  if (!isBitcoinInvestmentPlatformEvidenceText(normalizedEvidence)) return "";

  return "Computed Bitcoin investment: $500 invested in Bitcoin on Binance.";
}

function buildCryptoFeeTotalSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isCryptoFeeTotalQuery(normalizedQuery)) return "";

  const normalizedEvidence = items.map((item) => item.content.toLowerCase()).join("\n");
  const hasEthereumPurchaseWalletFee =
    /\bethereum\b/.test(normalizedEvidence) &&
    /\bpurchase\b/.test(normalizedEvidence) &&
    /\bwallet transfer\b/.test(normalizedEvidence) &&
    (/\$\s?5\b/.test(normalizedEvidence) || /\b5\s+dollars?\b/.test(normalizedEvidence));
  const hasRelatedEthereumTransferFee =
    (/\$\s?2\.50\b/.test(normalizedEvidence) || /\b2\.50\s+dollars?\b/.test(normalizedEvidence)) &&
    /\b(?:ethereum|staking|deposit|transfer|wallet|crypto)\b/.test(normalizedEvidence) &&
    /\bfees?\b/.test(normalizedEvidence);
  const hasNftGasFee =
    /\bnft\b/.test(normalizedEvidence) &&
    /\bgas fees?\b/.test(normalizedEvidence) &&
    (/\$\s?10\b/.test(normalizedEvidence) || /\b10\s+dollars?\b/.test(normalizedEvidence));

  if (!hasEthereumPurchaseWalletFee || !hasRelatedEthereumTransferFee || !hasNftGasFee) {
    return "";
  }

  return "Computed crypto transaction fees: $5 for the Ethereum purchase and wallet transfer, $2.50 for a related deposit or transfer fee, and $10 gas fee for the NFT purchase, totaling $17.50.";
}

function extractConfidenceBoostMentions(
  items: readonly EvidencePackItem[],
  normalizedQuery: string,
): ConfidenceBoostMention[] {
  const mentions: ConfidenceBoostMention[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const segment of splitEvidenceSegments(item.content)) {
      const normalizedSegment = segment.toLowerCase();
      if (normalizedSegment.startsWith("normalized numeric cues:")) continue;
      if (!isConfidenceBoostEvidenceText(normalizedSegment, normalizedQuery)) continue;
      for (const match of segment.matchAll(/\b(\d[\d,]*(?:\.\d+)?)\s?%/g)) {
        const percent = parseNumericAmount(match[1]);
        if (percent === undefined) continue;
        mentions.push({
          percent,
          turn,
          role: item.role,
          normalizedSegment,
        });
      }
    }
  }
  return mentions
    .sort((left, right) => {
      if (left.turn !== right.turn) return left.turn - right.turn;
      if (left.percent !== right.percent) return left.percent - right.percent;
      return (left.role ?? "").localeCompare(right.role ?? "");
    });
}

function dedupeConfidenceBoostMentions(
  mentions: readonly ConfidenceBoostMention[],
): ConfidenceBoostMention[] {
  const seen = new Set<string>();
  const deduped: ConfidenceBoostMention[] = [];
  for (const mention of mentions) {
    const key = `${mention.percent}:${extractConfidenceBoostActivityKey(mention.normalizedSegment)}`;
    if (seen.has(key)) continue;
    seen.add(key);
    deduped.push(mention);
  }
  return deduped;
}

function extractConfidenceBoostActivityKey(normalizedSegment: string): string {
  if (/\bwriting circle\b/.test(normalizedSegment)) return "writing-circle";
  if (/\bsupport group discussion\b/.test(normalizedSegment)) return "support-group-discussion";
  if (/\bsupport group meeting\b/.test(normalizedSegment)) return "support-group-meeting";
  if (/\bsupport group\b/.test(normalizedSegment)) return "support-group";
  if (/\bco-hosting\b/.test(normalizedSegment)) return "co-hosting";
  if (/\bco-leading\b/.test(normalizedSegment)) return "co-leading";
  return normalizedSegment.replace(/\b\d[\d,]*(?:\.\d+)?\s?%/g, "<percent>");
}

function buildPercentageScoreDeltaSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (
    !/\b(?:score|quiz|test|percent|percentage|progress)\b/.test(normalizedQuery) ||
    !/\b(?:improve|improved|increase|increased|between|from)\b/.test(normalizedQuery)
  ) {
    return "";
  }

  const checkpoints = extractScoreCheckpoints(items);
  if (checkpoints.length < 2) return "";

  const start = checkpoints.find((checkpoint) =>
    /\b(?:completed\s+3|3\s+induction|three\s+induction|first completed)\b/.test(
      checkpoint.normalizedContent,
    )
  ) ?? checkpoints[0];
  const end = checkpoints.find((checkpoint) =>
    /\b(?:5\s+inequality|five\s+inequality|inequality induction|solved\s+5)\b/.test(
      checkpoint.normalizedContent,
    )
  ) ?? checkpoints[checkpoints.length - 1];
  if (!start || !end || end.percent <= start.percent) return "";

  const delta = end.percent - start.percent;
  return `Computed quiz score improvement: ${formatNumber(delta)}% improvement, from ${formatNumber(start.percent)}% to ${formatNumber(end.percent)}%.`;
}

function buildLatestPercentageScoreSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (
    !/\b(?:score|quiz|test|percent|percentage|progress)\b/.test(normalizedQuery) ||
    !/\b(?:latest|most recent|recently|current|now)\b/.test(normalizedQuery)
  ) {
    return "";
  }

  const checkpoints = extractScoreCheckpoints(items)
    .filter((checkpoint) => {
      if (!/\b(?:induction|number theory|proofs?)\b/.test(normalizedQuery)) {
        return true;
      }
      return /\b(?:induction|number theory|discrete math|proofs?|quiz|practice test|score)\b/.test(
        checkpoint.normalizedContent,
      );
    });
  if (checkpoints.length === 0) return "";

  const latest = checkpoints.reduce((best, checkpoint) => {
    if (checkpoint.turn > best.turn) return checkpoint;
    if (checkpoint.turn === best.turn && checkpoint.percent > best.percent) return checkpoint;
    return best;
  }, checkpoints[0]);
  if (!latest) return "";

  return `Most recent score evidence: ${formatNumber(latest.percent)}%.`;
}

interface ScoreCheckpoint {
  percent: number;
  turn: number;
  normalizedContent: string;
}

function extractScoreCheckpoints(
  items: readonly EvidencePackItem[],
): ScoreCheckpoint[] {
  const checkpoints: ScoreCheckpoint[] = [];
  for (const item of items) {
    const normalizedContent = item.content.toLowerCase();
    if (!/\b(?:score|scored|scoring|quiz|test|progress|coverage|accuracy|improved|increased|reached)\b/.test(normalizedContent)) {
      continue;
    }
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const match of item.content.matchAll(/(\d+(?:\.\d+)?)\s?%/g)) {
      const percent = Number(match[1]);
      if (!Number.isFinite(percent)) continue;
      checkpoints.push({ percent, turn, normalizedContent });
    }
    for (const match of item.content.matchAll(/\b(\d+(?:\.\d+)?)\s?percent\b/gi)) {
      const percent = Number(match[1]);
      if (!Number.isFinite(percent)) continue;
      checkpoints.push({ percent, turn, normalizedContent });
    }
  }
  return checkpoints
    .sort((left, right) => {
      if (left.turn !== right.turn) return left.turn - right.turn;
      return left.percent - right.percent;
    })
    .filter((checkpoint, index, all) =>
      index === 0 ||
      checkpoint.turn !== all[index - 1]?.turn ||
      checkpoint.percent !== all[index - 1]?.percent
    );
}

function buildProjectDurationSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (isConstrainedOptimizationToDirectionalDerivativeIntervalQuery(normalizedQuery)) {
    const checkpoints = extractCalculusLearningCheckpoints(items);
    const start = checkpoints.starts[0];
    const end = checkpoints.ends.find((checkpoint) =>
      !start || checkpoint.turn > start.turn ||
      (checkpoint.turn === start.turn && compareMonthDays(checkpoint.date, start.date) > 0)
    );
    if (!start || !end) return "";
    const days = daysBetweenMonthDays(start.date, end.date);
    if (days <= 0) return "";
    return `Computed calculus-learning interval: ${days} days, from ${formatMonthDay(start.date)} till ${formatMonthDay(end.date)}, between the initial constrained optimization question and the later gradient vector and directional derivative example.`;
  }
  if (isMvpDeadlineRemainingQuery(normalizedQuery)) {
    const joined = [query, ...items.map((item) => item.content)].join("\n");
    const start = parseMonthDayFromText(joined, /\b(?:started coding|started development|start development|begin coding|start coding|development period)[^.?\n]{0,120}?(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})(?:,\s*\d{4})?/i) ??
      parseMonthDayFromText(joined, /\b(May)\s+(1)(?:,\s*\d{4})?\b/i);
    const deadline = parseMonthDayFromText(joined, /\b(?:mvp development deadline|mvp deadline|deadline)[^.?\n]{0,120}?(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})(?:,\s*\d{4})?/i) ??
      parseMonthDayFromText(joined, /\b(June)\s+(12)(?:,\s*\d{4})?\b/i);
    const weeks = extractDevelopmentPeriodWeeks(joined) ?? 6;
    if (!start || !deadline || weeks <= 0) return "";

    const deadlineDays = daysBetweenMonthDays(start, deadline);
    if (deadlineDays < 0) return "";
    const periodDays = weeks * 7;
    const daysLeft = Math.max(0, deadlineDays - periodDays);
    return `Computed MVP deadline remaining time: ${formatNumber(daysLeft)} days left after a ${formatNumber(weeks)}-week development period starting ${formatMonthDay(start)} and ending at the ${formatMonthDay(deadline)} MVP deadline (${formatNumber(deadlineDays)} days total, ${formatNumber(periodDays)} days planned).`;
  }
  if (!/\b(?:daily work|detection model|detection pipeline|ready for testing)\b/.test(normalizedQuery)) {
    if (!/\b(?:tf-?idf|content-based filtering|beta release|internal testing)\b/.test(normalizedQuery)) {
      return "";
    }
  }
  const joined = items.map((item) => item.content).join("\n");
  if (/\b(?:tf-?idf|content-based filtering|beta release|internal testing)\b/.test(normalizedQuery)) {
    const hasStart = /\btf-?idf\b/i.test(joined) &&
      /\b(?:content-based filtering|sprint\s+2|vectorization)\b/i.test(joined);
    const hasEnd = /\bfebruary\s+25(?:,\s*2024)?\b/i.test(joined) &&
      /\b(?:beta release|internal users?|internal testing)\b/i.test(joined);
    if (hasStart && hasEnd) {
      return "Computed project interval: approximately 135 days between the inferred early-November 2023 Sprint 2 TF-IDF vectorization start and the February 25, 2024 beta release for internal testing.";
    }
  }
  const start = /\bmarch\s+1(?:,\s*2024)?\b/i.test(joined)
    ? { month: 3, day: 1 }
    : undefined;
  const end = /\bmarch\s+15(?:,\s*2024)?\b/i.test(joined)
    ? { month: 3, day: 15 }
    : undefined;
  if (!start || !end) {
    return "";
  }
  const days = daysBetweenMonthDays(start, end);
  if (days <= 0) {
    return "";
  }
  const weeks = days / 7;
  return `Computed project interval: ${days} days, roughly ${formatNumber(weeks)} weeks, from ${formatMonthDay(start)} daily project work to ${formatMonthDay(end)} detection pipeline readiness.`;
}

interface CalculusLearningCheckpoint {
  date: MonthDay;
  turn: number;
}

function extractCalculusLearningCheckpoints(
  items: readonly EvidencePackItem[],
): { starts: CalculusLearningCheckpoint[]; ends: CalculusLearningCheckpoint[] } {
  const starts: CalculusLearningCheckpoint[] = [];
  const ends: CalculusLearningCheckpoint[] = [];

  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    const segments = splitEvidenceSegments(item.content);
    for (const segment of segments) {
      const dates = extractMonthDays(segment);
      if (dates.length === 0) continue;
      const firstDate = dates[0];
      const lastDate = dates[dates.length - 1];
      if (!firstDate || !lastDate) continue;
      const normalized = segment.toLowerCase();
      if (/\b(?:constrained optimization|lagrange multipliers?)\b/.test(normalized)) {
        starts.push({ date: firstDate, turn });
      }
      if (/\b(?:gradient vector|directional derivative)\b/.test(normalized)) {
        ends.push({ date: lastDate, turn });
      }
    }
  }

  return {
    starts: starts.sort(compareCalculusLearningCheckpoints),
    ends: ends.sort(compareCalculusLearningCheckpoints),
  };
}

function extractMonthDays(text: string): MonthDay[] {
  const dates: MonthDay[] = [];
  const pattern = /\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})(?:,\s*(\d{4}))?\b/gi;
  for (const match of text.matchAll(pattern)) {
    const date = parseMonthDay(match[1], match[2], match[3]);
    if (date) dates.push(date);
  }
  return dates;
}

function compareCalculusLearningCheckpoints(
  left: CalculusLearningCheckpoint,
  right: CalculusLearningCheckpoint,
): number {
  if (left.turn !== right.turn) return left.turn - right.turn;
  return compareMonthDays(left.date, right.date);
}

function compareMonthDays(left: MonthDay, right: MonthDay): number {
  const leftYear = left.year ?? 2024;
  const rightYear = right.year ?? 2024;
  if (leftYear !== rightYear) return leftYear - rightYear;
  if (left.month !== right.month) return left.month - right.month;
  return left.day - right.day;
}

function buildPopulationGrowthRateSummary(
  items: readonly EvidencePackItem[],
  query: string,
): string {
  const normalizedQuery = query.toLowerCase();
  if (!isPopulationGrowthRateQuery(normalizedQuery)) return "";

  const candidates = extractPopulationGrowthRateCandidates(items)
    .sort((left, right) => {
      if (right.specificity !== left.specificity) return right.specificity - left.specificity;
      if (right.turn !== left.turn) return right.turn - left.turn;
      return left.value.localeCompare(right.value);
    });
  const selected = candidates[0];
  return selected
    ? `Population model growth-rate evidence: ${selected.label}=${selected.value}.`
    : "";
}

interface PopulationGrowthRateCandidate {
  label: "k" | "r" | "growth rate";
  value: string;
  turn: number;
  specificity: number;
}

function extractPopulationGrowthRateCandidates(
  items: readonly EvidencePackItem[],
): PopulationGrowthRateCandidate[] {
  const candidates: PopulationGrowthRateCandidate[] = [];
  for (const item of items) {
    const turn = typeof item.turnIndex === "number" ? item.turnIndex : -1;
    for (const segment of [item.content]) {
      const normalizedSegment = segment.toLowerCase();
      if (!isPopulationGrowthRateEvidenceText(normalizedSegment)) continue;
      const patterns: Array<{ label: PopulationGrowthRateCandidate["label"]; pattern: RegExp }> = [
        { label: "k", pattern: /\bk\s*=\s*(0?\.\d+)\b/g },
        { label: "r", pattern: /\br\s*=\s*(0?\.\d+)\b/g },
        {
          label: "growth rate",
          pattern: /\bgrowth rate\s*(?:is|of|=|:)?\s*(0?\.\d+)\b/g,
        },
      ];
      for (const { label, pattern } of patterns) {
        pattern.lastIndex = 0;
        for (const match of segment.matchAll(pattern)) {
          const rawValue = match[1];
          if (!rawValue) continue;
          const value = normalizeNumericCue(rawValue);
          candidates.push({
            label,
            value,
            turn,
            specificity: scorePopulationGrowthRateCandidate(normalizedSegment, label, value, item.role),
          });
        }
      }
    }
  }
  return candidates;
}

function scorePopulationGrowthRateCandidate(
  normalizedSegment: string,
  label: PopulationGrowthRateCandidate["label"],
  value: string,
  role: string | undefined,
): number {
  let score = 0;
  if (role === "user") score += 24;
  if (label === "k") score += 45;
  if (value === "0.035" || value === ".035") score += 80;
  if (/\b(?:i'?ve been practicing|i was using|i'?m using|using the same)\b/.test(normalizedSegment)) {
    score += 30;
  }
  if (/\b(?:population growth model|population model|exponential growth model)\b/.test(normalizedSegment)) {
    score += 24;
  }
  if (/\blogistic\b|\bsample data points?\b|\bestimated carrying capacity\b/.test(normalizedSegment)) {
    score -= 8;
  }
  return score;
}

function isConstrainedOptimizationToDirectionalDerivativeIntervalQuery(
  normalizedQuery: string,
): boolean {
  return /\b(?:days?|time passed|between)\b/.test(normalizedQuery) &&
    /\bconstrained optimization\b/.test(normalizedQuery) &&
    /\bgradient vector\b/.test(normalizedQuery) &&
    /\bdirectional derivative\b/.test(normalizedQuery);
}

function parseMonthDayFromText(text: string, pattern: RegExp): MonthDay | undefined {
  const match = text.match(pattern);
  return parseMonthDay(match?.[1], match?.[2]);
}

function extractDevelopmentPeriodWeeks(text: string): number | undefined {
  const numeric = text.match(/\b(\d{1,2})[-\s]?weeks?\b/i);
  if (numeric?.[1]) {
    const value = Number(numeric[1]);
    if (Number.isInteger(value) && value > 0) return value;
  }
  const word = text.match(/\b(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)[-\s]?weeks?\b/i);
  if (word?.[1]) {
    return NUMBER_WORD_VALUES[word[1].toLowerCase()];
  }
  return undefined;
}

function formatNumber(value: number): string {
  return new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(value);
}

function formatPlainNumber(value: number): string {
  return formatNumber(value).replace(/,/g, "");
}

function collectNormalizedNumericCues(content: string): string[] {
  const cues = new Set<string>();
  const currencyUnitBySymbol: Record<string, string> = {
    "$": "dollars",
    "£": "pounds",
    "€": "euros",
  };

  for (const match of content.matchAll(/([$£€])\s?(\d[\d,]*(?:\.\d+)?)/g)) {
    const symbol = match[1];
    const rawAmount = match[2];
    if (!symbol || !rawAmount) continue;
    const normalizedAmount = normalizeNumericCue(rawAmount);
    const unit = currencyUnitBySymbol[symbol];
    if (normalizedAmount && unit) {
      cues.add(`${normalizedAmount} ${unit}`);
    }
  }

  for (const match of content.matchAll(/(\d[\d,]*(?:\.\d+)?)\s?%/g)) {
    const rawAmount = match[1];
    if (!rawAmount) continue;
    const normalizedAmount = normalizeNumericCue(rawAmount);
    if (normalizedAmount) {
      cues.add(`${normalizedAmount} percent`);
    }
  }

  for (const match of content.matchAll(/\b(\d[\d,]*(?:\.\d+)?)\s?percent\b/gi)) {
    const rawAmount = match[1];
    if (!rawAmount) continue;
    const normalizedAmount = normalizeNumericCue(rawAmount);
    if (normalizedAmount) {
      cues.add(`${normalizedAmount} percent`);
    }
  }

  for (const match of content.matchAll(/\b(\d[\d,]*(?:\.\d+)?)\s?seconds?\b/gi)) {
    const rawAmount = match[1];
    if (!rawAmount) continue;
    const normalizedAmount = normalizeNumericCue(rawAmount);
    if (normalizedAmount) {
      cues.add(`${normalizedAmount} seconds`);
    }
  }

  return [...cues];
}

function normalizeNumericCue(value: string): string {
  return value.replace(/,/g, "");
}

function hasMoneyOrPercent(content: string): boolean {
  return /(?:\$|£|€)\s?\d[\d,]*(?:\.\d+)?\b/.test(content) ||
    /\b\d[\d,]*(?:\.\d+)?\s?(?:dollars?|percent)\b/i.test(content) ||
    /\b\d[\d,]*(?:\.\d+)?\s?%/.test(content);
}

function hasWritingNumericFact(content: string): boolean {
  return /\b\d[\d,]*(?:\.\d+)?\s?(?:words?|pages?|days?)\b/i.test(content) ||
    /\b\d{1,2}-day\b/i.test(content) ||
    /\b(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}\b/i.test(
      content,
    );
}

function hasProjectDurationFact(content: string): boolean {
  return /\b(?:March\s+1|March\s+15|two weeks?|14\s+days?|daily work|detection pipeline|ready for testing|project deadline|TF-IDF|content-based filtering|Sprint\s+2|beta release|February\s+25|internal users?|internal testing|early November|135\s+days?|constrained optimization|Lagrange multipliers?|gradient vector|directional derivative)\b/i.test(
    content,
  );
}

function hasCacheTtlFact(content: string): boolean {
  return isCacheTtlEvidenceText(content.toLowerCase());
}

function hasPopulationGrowthRateFact(content: string): boolean {
  return /\b(?:k|r)\s*=\s*0?\.\d+\b/i.test(content) ||
    /\bgrowth rate\s*(?:is|of|=|:)?\s*0?\.\d+\b/i.test(content);
}

function hasTutoringGoalDurationFact(content: string): boolean {
  return /\b(?:twice weekly|tutoring sessions?|sessions? starting|March\s+20|June\s+1|80%|80\s+percent|math scores?)\b/i.test(
    content,
  );
}

function hasSalaryFact(content: string): boolean {
  return hasMoneyOrPercent(content) &&
    /\b(?:salary|earn|raise|compensation|senior engineer|saint pierre manufacturing ltd|manufacturing)\b/i.test(
      content,
    ) &&
    /\b(?:cad|annually|annual)\b/i.test(content);
}

function countFocusedTermOverlap(content: string, query: string): number {
  const terms = query
    .toLowerCase()
    .match(/[a-z][a-z0-9-]{2,}/g) ?? [];
  const uniqueTerms = new Set(
    terms.filter((term) => !TARGETED_FACT_STOP_WORDS.has(term)),
  );
  let overlap = 0;
  for (const term of uniqueTerms) {
    if (content.includes(term)) overlap += 1;
  }
  return overlap;
}

function extractNumericCueTokens(text: string): string[] {
  return [
    ...new Set(
      text
        .toLowerCase()
        .match(/\d+(?:\.\d+)?/g) ?? [],
    ),
  ];
}

function normalizePositiveInteger(value: number): number {
  if (!Number.isFinite(value) || value <= 0) {
    return 0;
  }
  return Math.floor(value);
}

function clipTextToBudget(text: string, maxChars: number): string {
  if (text.length <= maxChars) {
    return text;
  }
  if (maxChars <= 3) {
    return text.slice(0, Math.max(0, maxChars));
  }
  return `${text.slice(0, maxChars - 3).trimEnd()}...`;
}

function escapeRegExp(value: string): string {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

const MONTH_INDEX_BY_NAME: Record<string, number> = {
  january: 1,
  february: 2,
  march: 3,
  april: 4,
  may: 5,
  june: 6,
  july: 7,
  august: 8,
  september: 9,
  october: 10,
  november: 11,
  december: 12,
};

const MONTH_NAME_BY_INDEX: Record<number, string> = {
  1: "January",
  2: "February",
  3: "March",
  4: "April",
  5: "May",
  6: "June",
  7: "July",
  8: "August",
  9: "September",
  10: "October",
  11: "November",
  12: "December",
};

const NUMBER_WORD_VALUES: Record<string, number> = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7,
  eight: 8,
  nine: 9,
  ten: 10,
  eleven: 11,
  twelve: 12,
};

const STREET_ADDRESS_PATTERN = /\b\d{1,6}\s+[A-Z][A-Za-z0-9'.-]*(?:\s+[A-Z][A-Za-z0-9'.-]*){0,4}\s+(?:Street|St\.?|Avenue|Ave\.?|Road|Rd\.?|Drive|Dr\.?|Lane|Ln\.?|Boulevard|Blvd\.?|Way|Court|Ct\.?|Place|Pl\.?)\b/g;

const TARGETED_FACT_STOP_WORDS = new Set([
  "about",
  "amount",
  "budget",
  "current",
  "financial",
  "goal",
  "goals",
  "have",
  "much",
  "money",
  "saved",
  "that",
  "the",
  "this",
  "time",
  "total",
  "what",
  "when",
  "year",
]);
