import { dirname } from "node:path";
import type { ChangeAnalysis } from "./fingerprint.js";

export interface UpdateDecision {
  action: "SKIP" | "PARTIAL_UPDATE" | "ARCHITECTURE_UPDATE" | "FULL_UPDATE";
  filesToReanalyze: string[];
  rerunArchitecture: boolean;
  rerunTour: boolean;
  reason: string;
}

/**
 * Classify the type of graph update needed based on structural change analysis.
 *
 * Decision matrix:
 * - SKIP: all files NONE or COSMETIC only
 * - PARTIAL_UPDATE: some STRUCTURAL, same directories
 * - ARCHITECTURE_UPDATE: new/deleted directories or >10 structural files
 * - FULL_UPDATE: >30 structural files or >50% of total files changed structurally
 */
export function classifyUpdate(
  analysis: ChangeAnalysis,
  totalFilesInGraph: number,
  allKnownFiles: string[] = [],
): UpdateDecision {
  const { newFiles, deletedFiles, structurallyChangedFiles, cosmeticOnlyFiles } = analysis;
  const structuralCount = structurallyChangedFiles.length + newFiles.length + deletedFiles.length;

  // No structural changes at all — skip
  if (structuralCount === 0) {
    const cosmeticCount = cosmeticOnlyFiles.length;
    const reason = cosmeticCount > 0
      ? `${cosmeticCount} file(s) have cosmetic-only changes (no structural impact)`
      : "No changes detected";

    return {
      action: "SKIP",
      filesToReanalyze: [],
      rerunArchitecture: false,
      rerunTour: false,
      reason,
    };
  }

  // Too many structural changes — suggest full rebuild
  const triggeredByCount = structuralCount > 30;
  const triggeredByPercentage = totalFilesInGraph > 0 && structuralCount / totalFilesInGraph > 0.5;
  if (triggeredByCount || triggeredByPercentage) {
    const thresholdReason =
      triggeredByCount && triggeredByPercentage
        ? ">30 files and >50% of project"
        : triggeredByCount
          ? ">30 files"
          : ">50% of project";
    return {
      action: "FULL_UPDATE",
      filesToReanalyze: [...structurallyChangedFiles, ...newFiles],
      rerunArchitecture: true,
      rerunTour: true,
      reason: `${structuralCount} files have structural changes (${thresholdReason}) — full rebuild recommended`,
    };
  }

  // Check if directory structure changed (new/deleted top-level directories)
  const hasDirectoryChanges = detectDirectoryChanges(newFiles, deletedFiles, allKnownFiles);

  if (hasDirectoryChanges || structuralCount > 10) {
    return {
      action: "ARCHITECTURE_UPDATE",
      filesToReanalyze: [...structurallyChangedFiles, ...newFiles],
      rerunArchitecture: true,
      rerunTour: true,
      reason: hasDirectoryChanges
        ? `Directory structure changed (${newFiles.length} new, ${deletedFiles.length} deleted files)`
        : `${structuralCount} files have structural changes — architecture re-analysis needed`,
    };
  }

  // Localized structural changes — partial update
  return {
    action: "PARTIAL_UPDATE",
    filesToReanalyze: [...structurallyChangedFiles, ...newFiles],
    rerunArchitecture: false,
    rerunTour: false,
    reason: `${structuralCount} file(s) have structural changes: ${summarizeChanges(analysis)}`,
  };
}

/**
 * Detect if the changes affect the directory structure (new or removed directories).
 * Uses all known files in the project as the baseline for existing directories,
 * then checks if any new/deleted files introduce or remove a top-level source directory.
 */
function detectDirectoryChanges(
  newFiles: string[],
  deletedFiles: string[],
  allKnownFiles: string[],
): boolean {
  const existingDirs = new Set(
    allKnownFiles.map((f) => topDirectory(f)).filter(Boolean),
  );

  for (const f of newFiles) {
    const dir = topDirectory(f);
    if (dir && !existingDirs.has(dir)) return true;
  }

  for (const f of deletedFiles) {
    const dir = topDirectory(f);
    if (dir && !existingDirs.has(dir)) return true;
  }

  return false;
}

/**
 * Get the top-level directory of a file path (first path segment).
 */
function topDirectory(filePath: string): string | null {
  const dir = dirname(filePath);
  if (dir === "." || dir === "") return null;
  const segments = dir.split("/");
  return segments[0] || null;
}

/**
 * Produce a concise human-readable summary of structural changes.
 */
function summarizeChanges(analysis: ChangeAnalysis): string {
  const parts: string[] = [];

  if (analysis.newFiles.length > 0) {
    parts.push(`${analysis.newFiles.length} new`);
  }
  if (analysis.deletedFiles.length > 0) {
    parts.push(`${analysis.deletedFiles.length} deleted`);
  }
  if (analysis.structurallyChangedFiles.length > 0) {
    parts.push(`${analysis.structurallyChangedFiles.length} modified`);
  }

  return parts.join(", ");
}
