/**
 * SecretRedactor - redacts secrets from logs and evidence
 *
 * Implements DD §9.2 / §16.2.
 * Shared by PermissionEngine + Logger.
 *
 * @module packages/runtime/src/security/SecretRedactor
 */

export interface RedactionConfig {
  patterns: RegExp[]
  replacement: string
  preserve_format: boolean
}

export interface RedactionResult {
  redacted: string
  redactions: RedactionRecord[]
}

export interface RedactionRecord {
  type: string
  original: string
  redacted: string
  start: number
  end: number
}

// Default secret patterns (DD §16.2)
const DEFAULT_SECRET_PATTERNS = [
  // API Keys
  { name: 'openai', pattern: /\b(sk-[a-zA-Z0-9_-]{20,})\b/g },
  { name: 'anthropic', pattern: /\b(sk-ant-[a-zA-Z0-9_-]{20,})\b/g },
  { name: 'github', pattern: /\b(ghp_[a-zA-Z0-9_-]{36})\b/g },
  { name: 'aws_access', pattern: /\b(AKIA[0-9A-Z]{16})\b/g },
  { name: 'aws_secret', pattern: /\b([A-Za-z0-9/+=]{40})\b(?=.*aws)/g },
  { name: 'azure_key', pattern: /\b([a-zA-Z0-9+/]{86}==)\b/g },

  // Generic API keys
  { name: 'generic_api_key', pattern: /\b(api[_-]?key|apikey|api[_-]?secret)[=:\s]+["']?([a-zA-Z0-9_-]{16,})["']?/gi },

  // Bearer tokens
  { name: 'bearer_token', pattern: /\bBearer\s+[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },

  // Basic auth
  { name: 'basic_auth', pattern: /\bBasic\s+[a-zA-Z0-9+/]+=*\b/g },

  // Private keys
  { name: 'ssh_private', pattern: /(-{5}BEGIN[ A-Z]+PRIVATE KEY-{5})[\s\S]*?(-{5}END[ A-Z]+PRIVATE KEY-{5})/g },
  { name: 'pgp_private', pattern: /(-{5}BEGIN PGP PRIVATE KEY-{5})[\s\S]*?(-{5}END PGP PRIVATE KEY-{5})/g },

  // Database URLs
  { name: 'db_url', pattern: /\b(mysql|postgres|postgresql|mongodb|redis):\/\/[^:]+:[^@]+@[^\s"']+/g },

  // Environment variables with secrets
  { name: 'env_secret', pattern: /\b(AWS_|AZURE_|GITHUB_|ANTHROPIC_|OPENAI_|STRIPE_|SLACK_|TWILIO_)[A-Z_]*(=)[^\s"']+/gi },

  // JWT tokens
  { name: 'jwt', pattern: /\beyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*\b/g },

  // Generic secrets in quotes
  { name: 'quoted_secret', pattern: /["'`](?:secret|password|passwd|pwd|token|key|api[_-]?key)["'`]*\s*[:=]\s*["'`]([^"'`]{8,})["'`]/gi },
]

export class SecretRedactor {
  private patterns: Array<{ name: string; pattern: RegExp }>
  private replacement: string
  private preserve_format: boolean

  constructor(config?: Partial<RedactionConfig>) {
    this.patterns = DEFAULT_SECRET_PATTERNS.map(p => ({
      name: p.name,
      pattern: new RegExp(p.pattern.source, p.pattern.flags)
    }))
    this.replacement = config?.replacement ?? '[REDACTED]'
    this.preserve_format = config?.preserve_format ?? true
  }

  /**
   * Redact secrets from a string.
   */
  redact(input: string): RedactionResult {
    const redactions: RedactionRecord[] = []
    let result = input

    for (const { name, pattern } of this.patterns) {
      // Reset lastIndex for global patterns
      pattern.lastIndex = 0

      const matches: RegExpMatchArray | null = result.match(pattern)
      if (!matches) continue

      for (const match of matches) {
        const start = result.indexOf(match)
        const end = start + match.length

        // Build redacted version
        let redacted_value: string
        if (this.preserve_format) {
          redacted_value = this.preserve_match_format(match, name)
        } else {
          redacted_value = this.replacement
        }

        redactions.push({
          type: name,
          original: match,
          redacted: redacted_value,
          start,
          end
        })
      }

      // Actually replace in result
      pattern.lastIndex = 0
      result = result.replace(pattern, (match) => {
        const idx = redactions.findIndex(r => r.original === match && !r.redacted.startsWith('['))
        if (idx >= 0 && this.preserve_format) {
          return this.preserve_match_format(match, redactions[idx].type)
        }
        return this.preserve_format ? this.preserve_match_format(match, name) : this.replacement
      })
    }

    return { redacted: result, redactions }
  }

  /**
   * Redact a single value (for credential comparison).
   */
  redact_value(value: string): string {
    for (const { name, pattern } of this.patterns) {
      pattern.lastIndex = 0
      if (pattern.test(value)) {
        return this.preserve_match_format(value, name)
      }
    }
    return value
  }

  /**
   * Check if a string contains secrets.
   */
  contains_secrets(input: string): boolean {
    for (const { pattern } of this.patterns) {
      pattern.lastIndex = 0
      if (pattern.test(input)) {
        return true
      }
    }
    return false
  }

  /**
   * Add custom secret pattern.
   */
  add_pattern(name: string, pattern: RegExp): void {
    this.patterns.push({ name, pattern: new RegExp(pattern.source, pattern.flags) })
  }

  /**
   * Preserve format while redacting (e.g., sk-...xyz becomes sk-[REDACTED]xyz).
   */
  private preserve_match_format(match: string, _type: string): string {
    if (match.length <= 8) {
      return this.replacement
    }

    // Preserve first 4 chars if it's a key-like pattern
    const prefix_match = match.match(/^(sk-|ghp_|AKIA|Bearer\s|Basic\s|mysql:\/\/)/)
    if (prefix_match) {
      const prefix = prefix_match[1]
      const visible = Math.min(4, match.length - prefix.length - 4)
      return `${prefix}${this.replacement}${match.slice(-visible)}`
    }

    // Preserve email-like patterns
    const email_match = match.match(/^[^@]+@[^@]+\.[^@]+$/)
    if (email_match) {
      const parts = match.split('@')
      return `${parts[0].slice(0, 2)}***@***${parts[1].slice(-4)}`
    }

    // Default: show first 2 and last 2
    if (match.length > 8) {
      return `${match.slice(0, 2)}${this.replacement}${match.slice(-2)}`
    }

    return this.replacement
  }
}

export function createSecretRedactor(config?: Partial<RedactionConfig>): SecretRedactor {
  return new SecretRedactor(config)
}

// Shared instance for PermissionEngine + Logger
let shared_instance: SecretRedactor | undefined

export function get_shared_redactor(): SecretRedactor {
  if (!shared_instance) {
    shared_instance = new SecretRedactor()
  }
  return shared_instance
}