/**
 * PathClassifier - classifies file paths into 9 security categories
 *
 * Implements DD §9.2; security-model-v1.md.
 * Realpath normalization before prefix checks; .git/ internals protected.
 *
 * @module packages/runtime/src/security/PathClassifier
 */

import { realpathSync } from 'fs'
import { resolve, normalize, sep } from 'path'

export type PathCategory =
  | 'project'              // general project files
  | 'project_air_shared'   // .air/shared/
  | 'project_air_local'    // .air/local/
  | 'project_build'        // build outputs, artifacts
  | 'project_git'          // .git/ internals
  | 'project_outside_user' // project files outside user scope
  | 'system_sensitive'     // /etc, /usr, system directories
  | 'credential_store'     // ~/.ssh, ~/.gnupg, .env files
  | 'unknown'              // fallback

const CREDENTIAL_PATTERNS = [
  '.ssh', '.gnupg', '.gpg', '.aws', '.azure', '.kube',
  '.env', '.env.local', '.env.production', '.env.staging',
  '.npmrc', '.pypirc', '.dockercfg', '.docker/config.json',
  'credentials.json', 'service-account', '.netrc',
]

const SYSTEM_DIRS = ['/etc', '/usr', '/bin', '/sbin', '/lib', '/var', '/boot', '/sys', '/proc']
const BUILD_DIRS = ['dist', 'build', 'out', 'target', '.next', '.nuxt', '__pycache__']
const HOME_PATTERN = /^\/(home|Users|root)/

export interface ClassificationResult {
  category: PathCategory
  normalized_path: string
  is_symlink_escape: boolean
  reasons: string[]
}

/**
 * Classifies a path into one of 9 security categories.
 * Performs realpath normalization to detect symlink escapes.
 */
export class PathClassifier {
  private project_root: string

  constructor(project_root: string) {
    this.project_root = resolve(project_root)
  }

  classify(raw_path: string): ClassificationResult {
    const reasons: string[] = []
    let normalized: string
    let is_symlink_escape = false

    try {
      normalized = realpathSync(raw_path)
      if (resolve(raw_path) !== normalized) {
        is_symlink_escape = true
        reasons.push('symlink resolves outside its container')
      }
    } catch {
      normalized = resolve(raw_path)
    }

    // Check credential stores (highest security priority)
    if (this.is_credential_path(normalized)) {
      return { category: 'credential_store', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'credential store path'] }
    }

    // Check system directories
    if (this.is_system_path(normalized)) {
      return { category: 'system_sensitive', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'system directory'] }
    }

    const relative = this.relative_to_project(normalized)

    // Check if outside project tree
    if (!normalized.startsWith(this.project_root)) {
      return { category: 'project_outside_user', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'outside project tree'] }
    }

    // Check .git/ directory
    if (this.is_git_path(relative)) {
      return { category: 'project_git', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'git directory'] }
    }

    // Check .air/shared/
    if (this.is_air_shared_path(relative)) {
      return { category: 'project_air_shared', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'air shared directory'] }
    }

    // Check .air/local/
    if (this.is_air_local_path(relative)) {
      return { category: 'project_air_local', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'air local directory'] }
    }

    // Check build outputs
    if (this.is_build_output(normalized)) {
      return { category: 'project_build', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'build output'] }
    }

    // Default: project file
    return { category: 'project', normalized_path: normalized, is_symlink_escape, reasons: [...reasons, 'project file'] }
  }

  is_within_project(path: string): boolean {
    try {
      const resolved = resolve(path)
      return resolved.startsWith(this.project_root)
    } catch {
      return false
    }
  }

  private relative_to_project(path: string): string {
    if (path.startsWith(this.project_root)) {
      return path.slice(this.project_root.length + 1)
    }
    return path
  }

  private is_credential_path(path: string): boolean {
    const lower = path.toLowerCase()
    for (const pattern of CREDENTIAL_PATTERNS) {
      if (lower.includes(pattern.toLowerCase())) return true
    }
    return false
  }

  private is_system_path(path: string): boolean {
    return SYSTEM_DIRS.some((dir) => path.startsWith(dir))
  }

  private is_git_path(relative: string): boolean {
    const parts = relative.split(sep)
    return parts[0] === '.git' || parts.some((p) => p === '.git')
  }

  private is_air_shared_path(relative: string): boolean {
    return relative.startsWith(`.air${sep}shared`) || relative.startsWith('.air/shared')
  }

  private is_air_local_path(relative: string): boolean {
    return relative.startsWith(`.air${sep}local`) || relative.startsWith('.air/local')
  }

  private is_build_output(path: string): boolean {
    const parts = path.split(sep)
    return parts.some((part) => BUILD_DIRS.includes(part))
  }
}

export function createPathClassifier(project_root: string): PathClassifier {
  return new PathClassifier(project_root)
}
