/**
 * Suppression engine for config-audit.
 * Lets users suppress known false positives via .config-audit-ignore files.
 * Supports exact IDs (CA-CML-001) and glob patterns (CA-SET-*).
 * Zero external dependencies.
 */

import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { homedir } from 'node:os';

/**
 * Load suppressions from .config-audit-ignore files.
 * Searches targetPath first, then ~/.claude/config-audit/.
 * Project-level file takes precedence (loaded first).
 * @param {string} targetPath - Project root to search
 * @returns {Promise<{ suppressions: Array<{ pattern: string, comment: string }>, source: string }>}
 */
export async function loadSuppressions(targetPath) {
  const sources = [
    { path: join(targetPath, '.config-audit-ignore'), label: 'project' },
    { path: join(homedir(), '.config-audit', '.config-audit-ignore'), label: 'global' },
  ];

  for (const src of sources) {
    try {
      const content = await readFile(src.path, 'utf-8');
      const suppressions = parseIgnoreFile(content);
      return { suppressions, source: src.label };
    } catch {
      // File doesn't exist — try next
    }
  }

  return { suppressions: [], source: 'none' };
}

/**
 * Parse a .config-audit-ignore file into suppression entries.
 * @param {string} content - File content
 * @returns {Array<{ pattern: string, comment: string }>}
 */
export function parseIgnoreFile(content) {
  const suppressions = [];

  for (const rawLine of content.split('\n')) {
    const line = rawLine.trim();

    // Skip empty lines and comment-only lines
    if (!line || line.startsWith('#')) continue;

    // Split on first # for inline comment
    const hashIdx = line.indexOf('#');
    let pattern, comment;
    if (hashIdx > 0) {
      pattern = line.slice(0, hashIdx).trim();
      comment = line.slice(hashIdx + 1).trim();
    } else {
      pattern = line;
      comment = '';
    }

    // Validate pattern looks like a finding ID or glob
    if (/^CA-[A-Z]{2,4}[-*\d]+/.test(pattern) || /^CA-[A-Z]{2,4}-\*$/.test(pattern)) {
      suppressions.push({ pattern, comment });
    }
  }

  return suppressions;
}

/**
 * Apply suppressions to a findings array.
 * @param {object[]} findings - Array of finding objects with .id
 * @param {Array<{ pattern: string, comment: string }>} suppressions
 * @returns {{ active: object[], suppressed: object[] }}
 */
export function applySuppressions(findings, suppressions) {
  if (!suppressions || suppressions.length === 0) {
    return { active: [...findings], suppressed: [] };
  }

  const active = [];
  const suppressed = [];

  for (const f of findings) {
    if (isMatchedByAny(f.id, suppressions)) {
      suppressed.push(f);
    } else {
      active.push(f);
    }
  }

  return { active, suppressed };
}

/**
 * Check if a finding ID matches any suppression pattern.
 * @param {string} id - Finding ID (e.g. CA-CML-001)
 * @param {Array<{ pattern: string }>} suppressions
 * @returns {boolean}
 */
function isMatchedByAny(id, suppressions) {
  for (const s of suppressions) {
    if (matchPattern(id, s.pattern)) return true;
  }
  return false;
}

/**
 * Match a finding ID against a suppression pattern.
 * Supports exact match and glob-style CA-XXX-* patterns.
 * @param {string} id - e.g. "CA-CML-001"
 * @param {string} pattern - e.g. "CA-CML-001" or "CA-CML-*"
 * @returns {boolean}
 */
function matchPattern(id, pattern) {
  // Exact match
  if (id === pattern) return true;

  // Glob: CA-XXX-* matches any CA-XXX-NNN
  if (pattern.endsWith('-*')) {
    const prefix = pattern.slice(0, -1); // "CA-XXX-"
    return id.startsWith(prefix);
  }

  return false;
}

/**
 * Format a human-readable suppression summary line.
 * @param {object[]} suppressed - Array of suppressed findings
 * @returns {string}
 */
export function formatSuppressionSummary(suppressed) {
  if (!suppressed || suppressed.length === 0) {
    return '0 findings suppressed';
  }

  // Group by scanner prefix pattern
  const groups = new Map();
  for (const f of suppressed) {
    // Extract prefix: CA-CML-001 → CA-CML
    const prefix = f.id.replace(/-\d+$/, '');
    groups.set(prefix, (groups.get(prefix) || 0) + 1);
  }

  const parts = [];
  for (const [prefix, count] of groups) {
    parts.push(`${count} \u00d7 ${prefix}-*`);
  }

  return `${suppressed.length} finding(s) suppressed (${parts.join(', ')})`;
}
