/**
 * AI Context Formatter
 * Generates structured markdown for AI coding assistants to consume and fix security findings
 */

import type { ScanResult, Vulnerability, VulnerabilitySeverity } from '../../shared/types'
import { sortBySeverity } from './grouping'

/**
 * Options for AI context formatting
 */
export interface AIContextOptions {
  /** Maximum findings to include in detail (default: 20) */
  maxFindings?: number
  /** Include reference URLs (default: true) */
  includeReferences?: boolean
}

/**
 * Severity display order (higher priority first)
 */
const SEVERITY_ORDER: VulnerabilitySeverity[] = ['critical', 'high', 'medium', 'low', 'info']

/**
 * Get language hint from file extension
 */
function getLanguageFromPath(filePath: string): string {
  const ext = filePath.split('.').pop()?.toLowerCase() || ''
  const langMap: Record<string, string> = {
    ts: 'typescript',
    tsx: 'typescript',
    js: 'javascript',
    jsx: 'javascript',
    py: 'python',
    go: 'go',
    java: 'java',
    rb: 'ruby',
    php: 'php',
    yaml: 'yaml',
    yml: 'yaml',
    json: 'json',
  }
  return langMap[ext] || ''
}

/**
 * Group vulnerabilities by severity
 */
function groupBySeverity(vulnerabilities: Vulnerability[]): Map<VulnerabilitySeverity, Vulnerability[]> {
  const groups = new Map<VulnerabilitySeverity, Vulnerability[]>()

  // Initialize all severity groups
  for (const severity of SEVERITY_ORDER) {
    groups.set(severity, [])
  }

  // Group vulnerabilities
  for (const vuln of vulnerabilities) {
    const group = groups.get(vuln.severity) || []
    group.push(vuln)
    groups.set(vuln.severity, group)
  }

  return groups
}

/**
 * Get unique files for a list of vulnerabilities
 */
function getUniqueFiles(vulnerabilities: Vulnerability[]): string[] {
  const files = new Set<string>()
  for (const vuln of vulnerabilities) {
    files.add(vuln.filePath)
  }
  return Array.from(files).sort()
}

/**
 * Format a single finding for AI context
 */
function formatFinding(
  finding: Vulnerability,
  index: number,
  options: AIContextOptions
): string {
  const language = getLanguageFromPath(finding.filePath)
  let md = ''

  // Finding header
  md += `### ${index}. ${finding.title}\n\n`

  // Location
  md += `**File:** \`${finding.filePath}:${finding.lineNumber}\`\n`
  md += `**Category:** ${finding.category}\n`

  // Impact (if available)
  if (finding.impact) {
    md += `**Impact:** ${finding.impact}\n`
  }

  md += '\n'

  // Current code
  if (finding.lineContent && finding.lineContent.trim()) {
    md += `**Current Code:**\n`
    md += `\`\`\`${language}\n${finding.lineContent.trim()}\n\`\`\`\n\n`
  }

  // Fix instructions
  md += `**Fix Instructions:**\n`
  if (finding.fixSteps && finding.fixSteps.length > 0) {
    finding.fixSteps.forEach((step, i) => {
      md += `${i + 1}. ${step}\n`
    })
  } else if (finding.suggestedFix) {
    md += `1. ${finding.suggestedFix}\n`
  } else {
    md += `1. Review and fix the security issue described above\n`
  }
  md += '\n'

  // References (optional)
  if (options.includeReferences && finding.references && finding.references.length > 0) {
    md += `**References:**\n`
    for (const ref of finding.references) {
      md += `- ${ref}\n`
    }
    md += '\n'
  }

  return md
}

/**
 * Format findings for a specific severity level
 */
function formatSeveritySection(
  severity: VulnerabilitySeverity,
  findings: Vulnerability[],
  startIndex: number,
  maxFindings: number,
  options: AIContextOptions
): { markdown: string; count: number } {
  if (findings.length === 0) {
    return { markdown: '', count: 0 }
  }

  let md = `## ${severity.toUpperCase()} Issues\n\n`

  // Show findings up to max
  const toShow = findings.slice(0, maxFindings)
  const remaining = findings.length - toShow.length

  for (let i = 0; i < toShow.length; i++) {
    md += formatFinding(toShow[i], startIndex + i, options)
    md += '---\n\n'
  }

  // Indicate if more findings exist
  if (remaining > 0) {
    md += `> **Note:** ${remaining} more ${severity.toUpperCase()} issues not shown. Run \`oculum scan\` for full details.\n\n`
  }

  return { markdown: md, count: toShow.length }
}

/**
 * Format severity breakdown string
 */
function formatSeverityBreakdown(result: ScanResult): string {
  const parts: string[] = []
  const { severityCounts } = result

  if (severityCounts.critical > 0) parts.push(`${severityCounts.critical} critical`)
  if (severityCounts.high > 0) parts.push(`${severityCounts.high} high`)
  if (severityCounts.medium > 0) parts.push(`${severityCounts.medium} medium`)
  if (severityCounts.low > 0) parts.push(`${severityCounts.low} low`)
  if (severityCounts.info > 0) parts.push(`${severityCounts.info} info`)

  return parts.length > 0 ? `(${parts.join(', ')})` : ''
}

/**
 * Format summary table
 */
function formatSummaryTable(result: ScanResult): string {
  const { severityCounts, vulnerabilities } = result
  const byFile = new Map<string, Vulnerability[]>()

  // Group by file
  for (const vuln of vulnerabilities) {
    const file = vuln.filePath
    if (!byFile.has(file)) {
      byFile.set(file, [])
    }
    byFile.get(file)!.push(vuln)
  }

  let md = `## Summary\n\n`
  md += `| Severity | Count | Files |\n`
  md += `|----------|-------|-------|\n`

  for (const severity of SEVERITY_ORDER) {
    const count = severityCounts[severity]
    if (count > 0) {
      // Get unique files for this severity
      const filesForSeverity = getUniqueFiles(
        vulnerabilities.filter(v => v.severity === severity)
      )
      const fileList = filesForSeverity.slice(0, 3).join(', ')
      const moreFiles = filesForSeverity.length > 3 ? `, +${filesForSeverity.length - 3} more` : ''

      md += `| ${severity.charAt(0).toUpperCase() + severity.slice(1)} | ${count} | ${fileList}${moreFiles} |\n`
    }
  }

  return md + '\n'
}

/**
 * Format scan result as AI-ready context markdown
 *
 * @param result - The scan result to format
 * @param options - Formatting options
 * @returns Markdown string for AI consumption
 */
export function formatAIContext(result: ScanResult, options: AIContextOptions = {}): string {
  const { maxFindings = 20, includeReferences = true } = options
  const { vulnerabilities, repoName, timestamp } = result
  const totalCount = vulnerabilities.length

  let md = ''

  // Header
  md += `<!-- OCULUM AI CONTEXT - AUTO-GENERATED -->\n\n`
  md += `# Security Issues Requiring Fixes\n\n`
  md += `**Scan Date:** ${timestamp}\n`
  md += `**Repository:** ${repoName}\n`
  md += `**Findings:** ${totalCount} ${formatSeverityBreakdown(result)}\n\n`

  // No findings case
  if (totalCount === 0) {
    md += `---\n\n`
    md += `## No security issues found\n\n`
    md += `Great job! The scan found no security vulnerabilities.\n\n`
    md += `*Generated by Oculum Security Scanner*\n`
    return md
  }

  md += `---\n\n`

  // Instructions section
  md += `## Instructions for AI Assistants\n\n`
  md += `1. Address CRITICAL issues first, then HIGH\n`
  md += `2. Test each fix before moving to the next\n`
  md += `3. Follow the project's existing code patterns\n`
  md += `4. Mark findings as false positives with \`// oculum-ignore\` if not applicable\n\n`
  md += `---\n\n`

  // Sort vulnerabilities by severity
  const sorted = sortBySeverity(vulnerabilities)
  const grouped = groupBySeverity(sorted)

  // Track how many findings we've shown
  let shownCount = 0
  let remainingPerSeverity: { severity: VulnerabilitySeverity; count: number }[] = []

  // Format each severity section
  for (const severity of SEVERITY_ORDER) {
    const findings = grouped.get(severity) || []
    if (findings.length === 0) continue

    // Calculate how many we can show for this severity
    const availableSlots = Math.max(0, maxFindings - shownCount)
    const { markdown, count } = formatSeveritySection(
      severity,
      findings,
      shownCount + 1,
      availableSlots,
      { maxFindings: availableSlots, includeReferences }
    )

    md += markdown
    shownCount += count

    // Track remaining
    if (findings.length > count) {
      remainingPerSeverity.push({ severity, count: findings.length - count })
    }

    // Stop if we've hit the max
    if (shownCount >= maxFindings) break
  }

  // Add summary table
  md += formatSummaryTable(result)

  // Footer
  md += `*Generated by Oculum Security Scanner*\n`

  return md
}
