/**
 * Baseline Manager
 * Handles loading, saving, and clearing baseline files
 */

import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs'
import { join, dirname } from 'path'
import { execFileSync } from 'child_process'
import type { ScanResult, ScanDepth, Vulnerability } from '../types'
import type { BaselineData, BaselineFinding } from './types'
import { BASELINE_FILE_PATH, OCULUM_DIR } from './types'
import { computeFindingHash } from '../../postprocess/suppression/hash'

export interface BaselineManagerOptions {
  /** Project root path */
  projectPath: string
}

export interface LoadBaselineResult {
  /** Whether a baseline was found */
  found: boolean
  /** The baseline data (if found) */
  baseline?: BaselineData
  /** Error message (if failed to load) */
  error?: string
}

export interface SaveBaselineResult {
  /** Whether the save was successful */
  success: boolean
  /** Path where baseline was saved */
  path: string
  /** Error message (if failed) */
  error?: string
}

export interface ClearBaselineResult {
  /** Whether the clear was successful */
  success: boolean
  /** Whether a baseline existed before clearing */
  existed: boolean
  /** Error message (if failed) */
  error?: string
}

/**
 * Get current git commit SHA (short form)
 */
function getGitCommit(projectPath: string): string | undefined {
  try {
    const result = execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
      cwd: projectPath,
      stdio: ['ignore', 'pipe', 'ignore'],
    })
    return result.toString().trim()
  } catch {
    return undefined
  }
}

/**
 * Get current git branch name
 */
function getGitBranch(projectPath: string): string | undefined {
  try {
    const result = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
      cwd: projectPath,
      stdio: ['ignore', 'pipe', 'ignore'],
    })
    return result.toString().trim()
  } catch {
    return undefined
  }
}

/**
 * Convert a Vulnerability to a BaselineFinding
 */
function toBaselineFinding(vuln: Vulnerability): BaselineFinding {
  return {
    hash: computeFindingHash(vuln),
    filePath: vuln.filePath,
    lineNumber: vuln.lineNumber,
    category: vuln.category,
    severity: vuln.severity,
    title: vuln.title,
  }
}

/**
 * Manages baseline files for diff mode
 */
export class BaselineManager {
  private projectPath: string
  private baselinePath: string

  constructor(options: BaselineManagerOptions | string) {
    // Support both old string arg and new options object
    if (typeof options === 'string') {
      this.projectPath = options
    } else {
      this.projectPath = options.projectPath
    }
    this.baselinePath = join(this.projectPath, BASELINE_FILE_PATH)
  }

  /**
   * Get the full path to the baseline file
   */
  getBaselinePath(): string {
    return this.baselinePath
  }

  /**
   * Load baseline from .oculum/baseline.json
   */
  loadBaseline(): LoadBaselineResult {
    if (!existsSync(this.baselinePath)) {
      return { found: false }
    }

    try {
      const content = readFileSync(this.baselinePath, 'utf-8')
      const baseline = JSON.parse(content) as BaselineData

      // Basic validation
      if (baseline.version !== 1) {
        return {
          found: false,
          error: `Unsupported baseline version: ${baseline.version}. Expected version 1.`,
        }
      }

      if (!Array.isArray(baseline.findings)) {
        return {
          found: false,
          error: 'Invalid baseline: missing findings array',
        }
      }

      return { found: true, baseline }
    } catch (err) {
      return {
        found: false,
        error: `Failed to parse baseline: ${err instanceof Error ? err.message : 'Unknown error'}`,
      }
    }
  }

  /**
   * Save current scan result as baseline
   */
  saveBaseline(
    scanResult: ScanResult,
    options?: { commit?: string; branch?: string; scanDepth?: ScanDepth }
  ): SaveBaselineResult {
    try {
      // Ensure .oculum directory exists
      const oculumDir = join(this.projectPath, OCULUM_DIR)
      if (!existsSync(oculumDir)) {
        mkdirSync(oculumDir, { recursive: true })
      }

      // Get git info if not provided
      const commit = options?.commit ?? getGitCommit(this.projectPath)
      const branch = options?.branch ?? getGitBranch(this.projectPath)

      // Convert vulnerabilities to baseline findings
      const findings = scanResult.vulnerabilities.map(toBaselineFinding)

      // Build baseline data
      const baseline: BaselineData = {
        version: 1,
        createdAt: new Date().toISOString(),
        commit,
        branch,
        scanDepth: options?.scanDepth,
        findings,
        stats: {
          total: findings.length,
          critical: scanResult.severityCounts.critical,
          high: scanResult.severityCounts.high,
          medium: scanResult.severityCounts.medium,
          low: scanResult.severityCounts.low,
          info: scanResult.severityCounts.info,
        },
      }

      // Write to file
      writeFileSync(this.baselinePath, JSON.stringify(baseline, null, 2))

      return { success: true, path: this.baselinePath }
    } catch (err) {
      return {
        success: false,
        path: this.baselinePath,
        error: `Failed to save baseline: ${err instanceof Error ? err.message : 'Unknown error'}`,
      }
    }
  }

  /**
   * Clear (delete) the baseline file
   */
  clearBaseline(): ClearBaselineResult {
    const existed = existsSync(this.baselinePath)

    if (!existed) {
      return { success: true, existed: false }
    }

    try {
      unlinkSync(this.baselinePath)
      return { success: true, existed: true }
    } catch (err) {
      return {
        success: false,
        existed: true,
        error: `Failed to clear baseline: ${err instanceof Error ? err.message : 'Unknown error'}`,
      }
    }
  }

  /**
   * Check if a baseline exists
   */
  hasBaseline(): boolean {
    return existsSync(this.baselinePath)
  }
}
