// Parseur PUR d'un diff unifié git → hunks/lignes typées (testable sans DOM, modèle de
// parseWorktreePorcelain côté serveur). Consommé par DiffViewer (P8). Ne dépend de rien.

export type DiffLineType = 'context' | 'add' | 'del';

export interface DiffLine {
  type: DiffLineType;
  /** texte de la ligne sans le marqueur de tête (+/-/espace). */
  content: string;
  /** numéro de ligne côté ancien fichier (null pour une addition). */
  oldLine: number | null;
  /** numéro de ligne côté nouveau fichier (null pour une suppression). */
  newLine: number | null;
}

export interface DiffHunk {
  /** ligne d'en-tête `@@ -a,b +c,d @@ …`. */
  header: string;
  oldStart: number;
  newStart: number;
  lines: DiffLine[];
}

export interface ParsedDiff {
  hunks: DiffHunk[];
  /** compteurs agrégés (lignes ajoutées / supprimées). */
  additions: number;
  deletions: number;
}

const HUNK_RE = /^@@+ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;

/** Parse un diff unifié git (un seul fichier) en hunks. Tolérant : ignore les en-têtes de fichier. */
export function parseUnifiedDiff(diff: string): ParsedDiff {
  const hunks: DiffHunk[] = [];
  let additions = 0;
  let deletions = 0;
  let current: DiffHunk | null = null;
  let oldNo = 0;
  let newNo = 0;

  for (const raw of diff.split('\n')) {
    const m = HUNK_RE.exec(raw);
    if (m) {
      oldNo = Number(m[1]);
      newNo = Number(m[3]);
      current = { header: raw, oldStart: oldNo, newStart: newNo, lines: [] };
      hunks.push(current);
      continue;
    }
    if (!current) continue; // en-têtes (diff --git, index, ---, +++) avant le premier @@

    // marqueur git « \ No newline at end of file » : rattaché à la ligne précédente, ignoré.
    if (raw.startsWith('\\')) continue;

    const marker = raw[0];
    const content = raw.slice(1);
    if (marker === '+') {
      current.lines.push({ type: 'add', content, oldLine: null, newLine: newNo++ });
      additions++;
    } else if (marker === '-') {
      current.lines.push({ type: 'del', content, oldLine: oldNo++, newLine: null });
      deletions++;
    } else if (marker === ' ' || raw === '') {
      // ligne de contexte (ou ligne vide en contexte).
      current.lines.push({ type: 'context', content, oldLine: oldNo++, newLine: newNo++ });
    }
  }

  return { hunks, additions, deletions };
}
