import { VOCAB } from '@/lib/white-label/constants';

/**
 * ${VOCAB.factory} - Version History & Diff
 * Track generation versions and compare changes
 */

export interface GenerationVersion {
  id: string;
  taskId: string;
  prompt: string;
  status: string;
  artifactCount: number;
  language: string;
  database: string;
  createdAt: string;
  stages: Array<{ name: string; status: string; duration?: number }>;
  artifacts: VersionArtifact[];
}

export interface VersionArtifact {
  id: string;
  type: string;
  path: string;
  content: string;
  version: number;
}

export interface DiffLine {
  type: 'added' | 'removed' | 'unchanged';
  content: string;
  lineNumber: number;
}

export interface DiffResult {
  path: string;
  hunks: DiffHunk[];
  additions: number;
  deletions: number;
  isNew: boolean;
  isDeleted: boolean;
}

export interface DiffHunk {
  oldStart: number;
  newStart: number;
  lines: DiffLine[];
}

/**
 * Simple line-based diff algorithm
 */
export function computeDiff(oldContent: string, newContent: string): DiffLine[] {
  const oldLines = oldContent.split('\n');
  const newLines = newContent.split('\n');
  const result: DiffLine[] = [];

  // LCS-based diff
  const lcs = buildLCS(oldLines, newLines);
  let oldIdx = 0;
  let newIdx = 0;
  let lcsIdx = 0;
  let lineNum = 1;

  while (oldIdx < oldLines.length || newIdx < newLines.length) {
    if (lcsIdx < lcs.length && oldIdx < oldLines.length && oldLines[oldIdx] === lcs[lcsIdx] && newIdx < newLines.length && newLines[newIdx] === lcs[lcsIdx]) {
      result.push({ type: 'unchanged', content: oldLines[oldIdx], lineNumber: lineNum++ });
      oldIdx++;
      newIdx++;
      lcsIdx++;
    } else if (newIdx < newLines.length && (lcsIdx >= lcs.length || newLines[newIdx] !== lcs[lcsIdx])) {
      result.push({ type: 'added', content: newLines[newIdx], lineNumber: lineNum++ });
      newIdx++;
    } else if (oldIdx < oldLines.length && (lcsIdx >= lcs.length || oldLines[oldIdx] !== lcs[lcsIdx])) {
      result.push({ type: 'removed', content: oldLines[oldIdx], lineNumber: lineNum++ });
      oldIdx++;
    } else {
      break;
    }
  }

  return result;
}

function buildLCS(a: string[], b: string[]): string[] {
  const m = a.length;
  const n = b.length;
  // For very large files, limit LCS computation
  if (m > 500 || n > 500) {
    return simpleLCS(a, b);
  }
  const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
    }
  }

  const result: string[] = [];
  let i = m, j = n;
  while (i > 0 && j > 0) {
    if (a[i - 1] === b[j - 1]) {
      result.unshift(a[i - 1]);
      i--; j--;
    } else if (dp[i - 1][j] > dp[i][j - 1]) {
      i--;
    } else {
      j--;
    }
  }
  return result;
}

/** Simple LCS for large files - just find matching lines */
function simpleLCS(a: string[], b: string[]): string[] {
  const bSet = new Set(b);
  return a.filter(line => bSet.has(line));
}

/**
 * Compare two versions and produce per-file diffs
 */
export function compareVersions(
  oldVersion: GenerationVersion | null,
  newVersion: GenerationVersion
): DiffResult[] {
  const results: DiffResult[] = [];
  const oldArtifactMap = new Map<string, VersionArtifact>();

  if (oldVersion) {
    for (const a of oldVersion.artifacts) {
      oldArtifactMap.set(a.path, a);
    }
  }

  // Process new artifacts
  for (const newArt of newVersion.artifacts) {
    const oldArt = oldArtifactMap.get(newArt.path);
    if (!oldArt) {
      // New file
      const lines = newArt.content.split('\n');
      results.push({
        path: newArt.path,
        hunks: [{
          oldStart: 0,
          newStart: 1,
          lines: lines.map((l, i) => ({ type: 'added', content: l, lineNumber: i + 1 })),
        }],
        additions: lines.length,
        deletions: 0,
        isNew: true,
        isDeleted: false,
      });
    } else {
      // Modified file
      const diffLines = computeDiff(oldArt.content, newArt.content);
      const additions = diffLines.filter(l => l.type === 'added').length;
      const deletions = diffLines.filter(l => l.type === 'removed').length;
      if (additions > 0 || deletions > 0) {
        results.push({
          path: newArt.path,
          hunks: [{ oldStart: 1, newStart: 1, lines: diffLines }],
          additions,
          deletions,
          isNew: false,
          isDeleted: false,
        });
      }
      oldArtifactMap.delete(newArt.path);
    }
  }

  // Deleted files
  for (const [path, oldArt] of oldArtifactMap) {
    const lines = oldArt.content.split('\n');
    results.push({
      path,
      hunks: [{
        oldStart: 1,
        newStart: 0,
        lines: lines.map((l, i) => ({ type: 'removed', content: l, lineNumber: i + 1 })),
      }],
      additions: 0,
      deletions: lines.length,
      isNew: false,
      isDeleted: true,
    });
  }

  return results;
}
