// Deterministic extractive summary: pick the most "central" sentences of a
// lecture by term-frequency scoring, keeping them in chronological order so the
// summary still reads like a condensed version of the talk. No model.
//
// IMPORTANT: relative imports only inside src/lib (vitest has no '@/*' alias).

import type { Segment } from '../types';
import { splitSentences, tokenizeWords } from './tokenize';

/** A summary sentence tagged with where it came from (for click-to-seek). */
export interface SourcedSentence {
  text: string;
  /** Start time (seconds) of the source segment. */
  start: number;
  /** Source segment id, when the segment had one. */
  segmentId?: string;
}

/** Internal: a sentence carrying its source + original ordering keys. */
interface ScoredSentence extends SourcedSentence {
  /** Original position across the flattened doc, for stable chronological sort. */
  position: number;
  /** Term-frequency score, normalized by sentence length. */
  score: number;
}

/**
 * Summarize `segments` into at most `maxSentences` sentences.
 *
 * Algorithm (fully deterministic):
 *  1. Build a document term-frequency table over `tokenizeWords` of all text.
 *  2. Flatten every segment into sentences, each tagged with that segment's
 *     start + id, and a global position index.
 *  3. Score each sentence as the sum of its content words' document frequencies,
 *     normalized by the number of content words (so long sentences don't win by
 *     length alone). Sentences with no content words score 0.
 *  4. Take the top `maxSentences` by score (ties broken by earlier position),
 *     then RETURN them re-sorted into chronological order (start, then position).
 *
 * Empty input -> [].
 */
export function summarize(
  segments: Segment[],
  opts?: { maxSentences?: number },
): SourcedSentence[] {
  const maxSentences = opts?.maxSentences ?? 5;
  if (segments.length === 0 || maxSentences <= 0) return [];

  // 1. Document term frequencies.
  const freq = new Map<string, number>();
  for (const seg of segments) {
    for (const tok of tokenizeWords(seg.text)) {
      freq.set(tok, (freq.get(tok) ?? 0) + 1);
    }
  }

  // 2. Flatten into sentences with provenance + position.
  const sentences: ScoredSentence[] = [];
  let position = 0;
  for (const seg of segments) {
    for (const text of splitSentences(seg.text)) {
      // 3. Score this sentence.
      const tokens = tokenizeWords(text);
      let sum = 0;
      for (const tok of tokens) sum += freq.get(tok) ?? 0;
      const score = tokens.length > 0 ? sum / tokens.length : 0;

      sentences.push({
        text,
        start: seg.start,
        segmentId: seg.id,
        position: position++,
        score,
      });
    }
  }

  if (sentences.length === 0) return [];

  // 4a. Pick top-N by score, ties broken by earlier position (deterministic).
  const ranked = [...sentences].sort(
    (a, b) => b.score - a.score || a.position - b.position,
  );
  const picked = ranked.slice(0, maxSentences);

  // 4b. Restore chronological order: by start, then original position.
  picked.sort((a, b) => a.start - b.start || a.position - b.position);

  return picked.map(({ text, start, segmentId }) => ({ text, start, segmentId }));
}
