/**
 * Diff Detection Utility
 * Compares current repository tree against previous scan data to detect changed files
 * Used for incremental scanning (Story D)
 */

export interface TreeFileInfo {
  path: string
  sha: string
}

export interface DiffResult {
  /** Files that are new (not in previous scan) */
  added: string[]
  /** Files whose SHA changed (content modified) */
  modified: string[]
  /** Files that were deleted (in previous scan but not in current) */
  deleted: string[]
  /** Files that haven't changed */
  unchanged: string[]
  /** Total count of changed files (added + modified) */
  changedCount: number
  /** Whether this is a significant change warranting incremental scan */
  shouldUseIncremental: boolean
}

/**
 * Maximum number of changed files before falling back to full scan
 * Beyond this threshold, incremental mode provides diminishing returns
 */
const MAX_CHANGED_FILES_FOR_INCREMENTAL = 50

/**
 * Detect which files have changed between the current tree and previous scan
 *
 * @param currentTree - Array of files from current GitHub tree (path + sha)
 * @param previousFileShas - Map of {file_path: sha} from previous scan
 * @returns DiffResult with categorized file changes
 */
export function detectChangedFiles(
  currentTree: TreeFileInfo[],
  previousFileShas: Record<string, string> | null | undefined
): DiffResult {
  // If no previous data, everything is "new"
  if (!previousFileShas || Object.keys(previousFileShas).length === 0) {
    return {
      added: currentTree.map(f => f.path),
      modified: [],
      deleted: [],
      unchanged: [],
      changedCount: currentTree.length,
      shouldUseIncremental: false, // No baseline = full scan
    }
  }

  const added: string[] = []
  const modified: string[] = []
  const unchanged: string[] = []
  const currentPaths = new Set<string>()

  // Compare current files against previous
  for (const file of currentTree) {
    currentPaths.add(file.path)
    const previousSha = previousFileShas[file.path]

    if (!previousSha) {
      // File didn't exist before
      added.push(file.path)
    } else if (previousSha !== file.sha) {
      // File content changed (SHA differs)
      modified.push(file.path)
    } else {
      // Same SHA = unchanged
      unchanged.push(file.path)
    }
  }

  // Find deleted files (in previous but not in current)
  const deleted: string[] = []
  for (const path of Object.keys(previousFileShas)) {
    if (!currentPaths.has(path)) {
      deleted.push(path)
    }
  }

  const changedCount = added.length + modified.length
  const shouldUseIncremental = changedCount > 0 && changedCount <= MAX_CHANGED_FILES_FOR_INCREMENTAL

  return {
    added,
    modified,
    deleted,
    unchanged,
    changedCount,
    shouldUseIncremental,
  }
}

/**
 * Build a file SHA map from a GitHub tree response
 * Filters to only include blob (file) entries
 *
 * @param tree - GitHub tree items with path and sha
 * @returns Map of {file_path: sha}
 */
export function buildFileShaMap(
  tree: Array<{ path: string; sha: string; type: 'blob' | 'tree' }>
): Record<string, string> {
  const map: Record<string, string> = {}

  for (const item of tree) {
    // Only include files (blobs), not directories (trees)
    if (item.type === 'blob') {
      map[item.path] = item.sha
    }
  }

  return map
}

/**
 * Check if two tree SHAs are different
 * Quick check before doing detailed file comparison
 *
 * @param currentTreeSha - SHA of current tree
 * @param previousTreeSha - SHA from previous scan
 * @returns true if trees are different (need detailed comparison)
 */
export function hasTreeChanged(
  currentTreeSha: string,
  previousTreeSha: string | null | undefined
): boolean {
  if (!previousTreeSha) return true
  return currentTreeSha !== previousTreeSha
}
