// sarif-formatter.mjs — Converts scan-orchestrator envelope to SARIF 2.1.0
// OASIS SARIF standard: https://docs.oasis-open.org/sarif/sarif/v2.1.0/
// Zero external dependencies.

const SARIF_SCHEMA = 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json';
const SARIF_VERSION = '2.1.0';
const TOOL_NAME = 'llm-security';
const TOOL_URI = 'https://git.fromaitochitta.com/open/claude-code-llm-security';

/**
 * Map finding severity to SARIF level.
 * @param {string} severity - critical|high|medium|low|info
 * @returns {string} SARIF level: error|warning|note
 */
function toLevel(severity) {
  switch (severity) {
    case 'critical':
    case 'high':
      return 'error';
    case 'medium':
      return 'warning';
    case 'low':
    case 'info':
    default:
      return 'note';
  }
}

/**
 * Build SARIF rules array from unique finding scanner+title combos.
 * @param {object[]} findings
 * @returns {{ rules: object[], ruleIndex: Map<string, number> }}
 */
function buildRules(findings) {
  const ruleIndex = new Map();
  const rules = [];

  for (const f of findings) {
    const ruleId = `${f.scanner}/${f.title.replace(/\s+/g, '-').toLowerCase()}`;
    if (!ruleIndex.has(ruleId)) {
      ruleIndex.set(ruleId, rules.length);
      rules.push({
        id: ruleId,
        name: f.title,
        shortDescription: { text: f.title },
        fullDescription: { text: f.description || f.title },
        defaultConfiguration: { level: toLevel(f.severity) },
        properties: {
          tags: f.owasp ? [f.owasp] : [],
        },
      });
    }
  }

  return { rules, ruleIndex };
}

/**
 * Convert scan-orchestrator envelope JSON to SARIF 2.1.0 format.
 * @param {object} envelopeData - The full scan-orchestrator output
 * @param {string} [version='6.0.0'] - Tool version
 * @returns {object} SARIF 2.1.0 JSON
 */
export function toSARIF(envelopeData, version = '6.0.0') {
  // Collect all findings from all scanners
  const allFindings = [];
  if (envelopeData.scanners) {
    for (const scannerResult of Object.values(envelopeData.scanners)) {
      if (scannerResult.findings) {
        allFindings.push(...scannerResult.findings);
      }
    }
  }

  const { rules, ruleIndex } = buildRules(allFindings);

  // Build SARIF results
  const results = allFindings.map(f => {
    const ruleId = `${f.scanner}/${f.title.replace(/\s+/g, '-').toLowerCase()}`;
    const result = {
      ruleId,
      ruleIndex: ruleIndex.get(ruleId),
      level: toLevel(f.severity),
      message: { text: f.description || f.title },
      properties: {},
    };

    // Add OWASP tags
    if (f.owasp) {
      result.properties.tags = [f.owasp];
    }

    // Add recommendation
    if (f.recommendation) {
      result.properties.recommendation = f.recommendation;
    }

    // Add location if file is present
    if (f.file) {
      const location = {
        physicalLocation: {
          artifactLocation: { uri: f.file },
        },
      };
      if (f.line) {
        location.physicalLocation.region = { startLine: f.line };
      }
      result.locations = [location];
    }

    return result;
  });

  return {
    $schema: SARIF_SCHEMA,
    version: SARIF_VERSION,
    runs: [{
      tool: {
        driver: {
          name: TOOL_NAME,
          version,
          informationUri: TOOL_URI,
          rules,
        },
      },
      results,
    }],
  };
}
