/**
 * PII Redaction Library (Appendix D.1)
 * Detects and redacts personally identifiable information from logs and telemetry
 */

// =============================================================================
// PII Patterns
// =============================================================================

export interface RedactionPattern {
  name: string;
  pattern: RegExp;
  replacement: string;
}

export const PII_PATTERNS: RedactionPattern[] = [
  {
    name: 'email',
    pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
    replacement: '[REDACTED_EMAIL]',
  },
  {
    name: 'ssn',
    pattern: /\b\d{3}-\d{2}-\d{4}\b/g,
    replacement: '[REDACTED_SSN]',
  },
  {
    name: 'credit_card',
    pattern: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g,
    replacement: '[REDACTED_CC]',
  },
  {
    name: 'phone',
    pattern: /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
    replacement: '[REDACTED_PHONE]',
  },
  {
    name: 'ip_address',
    pattern: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
    replacement: '[REDACTED_IP]',
  },
  {
    name: 'password_token',
    pattern: /(?:password|token|secret|key|credential)s?[:\s=]+['"]?[^\s'"]{8,}['"]?/gi,
    replacement: '[REDACTED_SECRET]',
  },
  {
    name: 'jwt',
    pattern: /eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/g,
    replacement: '[REDACTED_JWT]',
  },
  {
    name: 'api_key',
    pattern: /(?:api[_-]?key|apikey)[:\s=]+['"]?[a-zA-Z0-9_-]{16,}['"]?/gi,
    replacement: '[REDACTED_API_KEY]',
  },
];

// =============================================================================
// Redaction Functions
// =============================================================================

export interface RedactionResult {
  redactedText: string;
  patternsMatched: string[];
  fieldsRedacted: string[];
  originalLength: number;
  redactedLength: number;
}

/**
 * Redact PII from a string
 */
export function redactPII(text: string): RedactionResult {
  const patternsMatched: string[] = [];
  const fieldsRedacted: string[] = [];

  let redactedText = text;
  const originalLength = text.length;

  for (const { name, pattern, replacement } of PII_PATTERNS) {
    const matches = text.match(pattern);
    if (matches) {
      patternsMatched.push(name);
      fieldsRedacted.push(...matches);
      redactedText = redactedText.replace(pattern, replacement);
    }
  }

  return {
    redactedText,
    patternsMatched: [...new Set(patternsMatched)],
    fieldsRedacted: [...new Set(fieldsRedacted)],
    originalLength,
    redactedLength: redactedText.length,
  };
}

/**
 * Redact PII from an object (recursively)
 */
export function redactObject<T extends Record<string, unknown>>(
  obj: T,
  sensitiveFields: string[] = ['password', 'token', 'secret', 'creditCard', 'ssn', 'email', 'phone']
): { redacted: T; metadata: RedactionResult } {
  const redacted: Record<string, unknown> = {};
  const allPatternsMatched: string[] = [];
  const allFieldsRedacted: string[] = [];

  for (const [key, value] of Object.entries(obj)) {
    if (typeof value === 'string') {
      // Check if field name suggests it's sensitive
      const isSensitiveField = sensitiveFields.some(sf =>
        key.toLowerCase().includes(sf.toLowerCase())
      );

      if (isSensitiveField) {
        redacted[key] = '[REDACTED_FIELD]';
        allFieldsRedacted.push(`${key}: ${value.substring(0, 20)}...`);
        allPatternsMatched.push('sensitive_field_name');
      } else {
        // Run PII detection on the value
        const result = redactPII(value);
        redacted[key] = result.redactedText;
        allPatternsMatched.push(...result.patternsMatched);
        allFieldsRedacted.push(...result.fieldsRedacted);
      }
    } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
      // Recursively redact nested objects
      const nested = redactObject(value as Record<string, unknown>, sensitiveFields);
      redacted[key] = nested.redacted;
      allPatternsMatched.push(...nested.metadata.patternsMatched);
      allFieldsRedacted.push(...nested.metadata.fieldsRedacted);
    } else {
      // Keep non-string values as-is
      redacted[key] = value;
    }
  }

  return {
    redacted: redacted as T,
    metadata: {
      redactedText: JSON.stringify(redacted),
      patternsMatched: [...new Set(allPatternsMatched)],
      fieldsRedacted: [...new Set(allFieldsRedacted)],
      originalLength: JSON.stringify(obj).length,
      redactedLength: JSON.stringify(redacted).length,
    },
  };
}

/**
 * Redact PII from log message
 */
export function redactLogMessage(
  message: string,
  context?: Record<string, unknown>
): { message: string; context?: Record<string, unknown>; redaction: RedactionResult } {
  const messageResult = redactPII(message);

  let redactedContext: Record<string, unknown> | undefined;
  let contextResult: RedactionResult | undefined;

  if (context) {
    const { redacted, metadata } = redactObject(context);
    redactedContext = redacted;
    contextResult = metadata;
  }

  return {
    message: messageResult.redactedText,
    context: redactedContext,
    redaction: {
      redactedText: messageResult.redactedText,
      patternsMatched: [
        ...messageResult.patternsMatched,
        ...(contextResult?.patternsMatched || []),
      ],
      fieldsRedacted: [...messageResult.fieldsRedacted, ...(contextResult?.fieldsRedacted || [])],
      originalLength: messageResult.originalLength + (contextResult?.originalLength || 0),
      redactedLength: messageResult.redactedLength + (contextResult?.redactedLength || 0),
    },
  };
}

/**
 * Check if text contains PII
 */
export function containsPII(text: string): boolean {
  return PII_PATTERNS.some(({ pattern }) => pattern.test(text));
}

/**
 * Get list of PII types found in text
 */
export function getPIITypes(text: string): string[] {
  const types: string[] = [];
  for (const { name, pattern } of PII_PATTERNS) {
    if (pattern.test(text)) {
      types.push(name);
    }
  }
  return [...new Set(types)];
}

// =============================================================================
// Custom Pattern Registration
// =============================================================================

const customPatterns: RedactionPattern[] = [];

/**
 * Register a custom PII pattern
 */
export function registerPIIPattern(pattern: RedactionPattern): void {
  customPatterns.push(pattern);
  // Also add to the main patterns list
  PII_PATTERNS.push(pattern);
}

/**
 * Remove a custom PII pattern by name
 */
export function unregisterPIIPattern(name: string): void {
  const index = PII_PATTERNS.findIndex(p => p.name === name);
  if (index !== -1 && customPatterns.some(p => p.name === name)) {
    PII_PATTERNS.splice(index, 1);
  }
}

// =============================================================================
// Preset Configurations
// =============================================================================

/**
 * Minimal redaction - only highly sensitive data
 */
export const minimalRedaction = {
  patterns: ['ssn', 'credit_card', 'password_token', 'jwt', 'api_key'],
  redactFields: ['password', 'secret', 'token', 'creditCard', 'ssn'],
};

/**
 * Standard redaction - all PII
 */
export const standardRedaction = {
  patterns: PII_PATTERNS.map(p => p.name),
  redactFields: ['password', 'secret', 'token', 'creditCard', 'ssn', 'email', 'phone', 'address'],
};

/**
 * Aggressive redaction - all PII + IP addresses + device IDs
 */
export const aggressiveRedaction = {
  patterns: [...PII_PATTERNS.map(p => p.name), 'device_id', 'session_id'],
  redactFields: [
    'password',
    'secret',
    'token',
    'creditCard',
    'ssn',
    'email',
    'phone',
    'address',
    'userId',
    'deviceId',
  ],
};
