// src/core/security/SecurityAuditService.ts

import type {
  IAuditEvent,
  IAuditEventSummary,
  AuditAction,
  AuditOutcome,
} from './IAuditEvent';
import type {
  IAuditService,
  IAuditRecordInput,
  IAuditQuery,
  IAuditQueryResult,
} from './IAuditService';
import { shouldAlwaysAudit } from './IAuditService';
import type { AuditLevel } from './ISupervisorSecurityPolicy';

/**
 * SecurityAuditService - In-memory audit trail implementation.
 *
 * Vision 27 Section 7: "All security-relevant events MUST be auditable"
 *
 * This implementation is suitable for:
 * - Development and testing
 * - Single-node deployments
 * - Demo applications
 *
 * For production/enterprise:
 * - Implement IAuditService with SIEM integration
 * - Add persistent storage
 * - Add cross-realm correlation
 *
 * See: Vision/27-SECURITY-FEDERATION-DRAFT.md Section 7
 */
export class SecurityAuditService implements IAuditService {
  private readonly _events: IAuditEvent[] = [];
  private readonly _maxEvents: number;

  /**
   * Create an audit service.
   *
   * @param maxEvents - Maximum events to retain (FIFO eviction)
   */
  constructor(maxEvents: number = 10000) {
    this._maxEvents = maxEvents;
  }

  // ═══════════════════════════════════════════════════════════════════════
  // RECORDING
  // ═══════════════════════════════════════════════════════════════════════

  /**
   * Record an audit event.
   *
   * Vision 27 Section 7.3 audit levels:
   * - 'none': Skip unless security-critical
   * - 'failures': Only denied/error outcomes
   * - 'summary': Basic info only
   * - 'full': Complete details
   */
  record(input: IAuditRecordInput, level: AuditLevel): string {
    // Check if we should audit this event
    if (!this._shouldRecord(input.action, input.outcome, level)) {
      return ''; // No event recorded
    }

    const eventId = this._generateEventId();
    const timestamp = Date.now();

    // Build delegation chain from capability
    const delegationChain = input.capability
      ? this._buildDelegationChain(input.capability.id)
      : undefined;

    // Create full event
    const event: IAuditEvent = {
      eventId,
      timestamp,
      principal: input.principal,
      capability: input.capability?.id,
      delegationChain,
      action: input.action,
      target: input.target,
      command: input.command,
      sourceRealm: input.sourceRealm,
      targetRealm: input.targetRealm,
      outcome: input.outcome,
      denialReason: input.denialReason,
      errorDetails: input.errorDetails,
    };

    // Store event (with FIFO eviction)
    this._events.push(event);
    if (this._events.length > this._maxEvents) {
      this._events.shift();
    }

    return eventId;
  }

  // ═══════════════════════════════════════════════════════════════════════
  // QUERYING
  // ═══════════════════════════════════════════════════════════════════════

  /**
   * Query audit trail.
   *
   * Vision 27 Section 7.2:
   * - WHO ACCESSED WHAT
   * - WHAT DID USER X DO
   */
  query(query: IAuditQuery): IAuditQueryResult {
    let results = this._events.filter((event) => this._matchesQuery(event, query));

    const totalCount = results.length;

    // Apply pagination
    if (query.offset !== undefined) {
      results = results.slice(query.offset);
    }
    if (query.limit !== undefined) {
      results = results.slice(0, query.limit);
    }

    return {
      events: results,
      totalCount,
      hasMore: query.limit !== undefined && totalCount > (query.offset ?? 0) + query.limit,
    };
  }

  /**
   * Get capability delegation chain.
   *
   * Vision 27 Section 7.2: "CAPABILITY LINEAGE"
   */
  getCapabilityLineage(capabilityId: string): IAuditEvent[] {
    // Find all events related to this capability or its ancestors
    const lineage: IAuditEvent[] = [];
    const visited = new Set<string>();
    let currentId: string | undefined = capabilityId;

    while (currentId && !visited.has(currentId)) {
      visited.add(currentId);

      // Find mint/attenuate events for this capability
      const events = this._events.filter(
        (e) =>
          e.capability === currentId &&
          (e.action === 'capability-mint' || e.action === 'capability-attenuate')
      );

      lineage.push(...events);

      // Look for parent capability
      const attenuateEvent = events.find((e) => e.action === 'capability-attenuate');
      if (attenuateEvent?.delegationChain && attenuateEvent.delegationChain.length > 0) {
        // Get parent from delegation chain
        currentId = attenuateEvent.delegationChain[attenuateEvent.delegationChain.length - 1];
      } else {
        currentId = undefined;
      }
    }

    return lineage;
  }

  /**
   * Get outcome statistics.
   */
  getOutcomeStats(fromTimestamp?: number, toTimestamp?: number): {
    success: number;
    denied: number;
    error: number;
  } {
    const filtered = this._events.filter((event) => {
      if (fromTimestamp !== undefined && event.timestamp < fromTimestamp) return false;
      if (toTimestamp !== undefined && event.timestamp > toTimestamp) return false;
      return true;
    });

    return {
      success: filtered.filter((e) => e.outcome === 'success').length,
      denied: filtered.filter((e) => e.outcome === 'denied').length,
      error: filtered.filter((e) => e.outcome === 'error').length,
    };
  }

  /**
   * Export audit trail.
   *
   * Vision 27 Section 7.2: Compliance reporting
   */
  export(query: IAuditQuery): IAuditEvent[] | IAuditEventSummary[] {
    const results = this.query(query).events;
    return results;
  }

  /**
   * Clear old events.
   */
  clearOlderThan(olderThan: number): number {
    const originalLength = this._events.length;
    const filtered = this._events.filter((e) => e.timestamp >= olderThan);

    // Replace events array contents
    this._events.length = 0;
    this._events.push(...filtered);

    return originalLength - this._events.length;
  }

  // ═══════════════════════════════════════════════════════════════════════
  // CONVENIENCE METHODS (for debugging/demos)
  // ═══════════════════════════════════════════════════════════════════════

  /**
   * Get all events (for debugging).
   */
  getAllEvents(): readonly IAuditEvent[] {
    return this._events;
  }

  /**
   * Get recent events.
   */
  getRecentEvents(count: number = 10): IAuditEvent[] {
    return this._events.slice(-count);
  }

  /**
   * Clear all events.
   */
  clear(): void {
    this._events.length = 0;
  }

  // ═══════════════════════════════════════════════════════════════════════
  // PRIVATE HELPERS
  // ═══════════════════════════════════════════════════════════════════════

  /**
   * Determine if event should be recorded based on audit level.
   *
   * Vision 27 Section 7.3:
   * - Security-critical actions ALWAYS logged
   * - Failures/denials ALWAYS logged
   * - Otherwise respects audit level
   */
  private _shouldRecord(
    action: AuditAction,
    outcome: AuditOutcome,
    level: AuditLevel
  ): boolean {
    // Security-critical actions and failures are ALWAYS logged
    if (shouldAlwaysAudit(action, outcome)) {
      return true;
    }

    // Apply audit level
    switch (level) {
      case 'none':
        return false;

      case 'failures':
        // Already handled above - only failures reach here
        return false;

      case 'summary':
      case 'full':
        return true;

      default:
        return false;
    }
  }

  /**
   * Check if event matches query.
   */
  private _matchesQuery(event: IAuditEvent, query: IAuditQuery): boolean {
    // Principal filter
    if (query.principal !== undefined && event.principal !== query.principal) {
      return false;
    }

    // Target pattern filter (supports * wildcard)
    if (query.targetPattern !== undefined) {
      const pattern = query.targetPattern
        .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
        .replace(/\*/g, '.*');
      const regex = new RegExp(`^${pattern}$`);
      if (!regex.test(event.target)) {
        return false;
      }
    }

    // Actions filter
    if (query.actions !== undefined && !query.actions.includes(event.action)) {
      return false;
    }

    // Outcome filter
    if (query.outcome !== undefined && event.outcome !== query.outcome) {
      return false;
    }

    // Source realm filter
    if (query.sourceRealm !== undefined && event.sourceRealm !== query.sourceRealm) {
      return false;
    }

    // Target realm filter
    if (query.targetRealm !== undefined && event.targetRealm !== query.targetRealm) {
      return false;
    }

    // Time range filter
    if (query.fromTimestamp !== undefined && event.timestamp < query.fromTimestamp) {
      return false;
    }
    if (query.toTimestamp !== undefined && event.timestamp > query.toTimestamp) {
      return false;
    }

    return true;
  }

  /**
   * Generate unique event ID.
   */
  private _generateEventId(): string {
    return `audit-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  }

  /**
   * Build delegation chain from capability events.
   */
  private _buildDelegationChain(capabilityId: string): string[] | undefined {
    const chain: string[] = [];
    let currentId: string | undefined = capabilityId;
    const visited = new Set<string>();

    while (currentId && !visited.has(currentId)) {
      visited.add(currentId);
      chain.push(currentId);

      // Find the capability event
      const capEvent = this._events.find(
        (e) =>
          e.capability === currentId &&
          (e.action === 'capability-mint' || e.action === 'capability-attenuate')
      );

      if (capEvent?.delegationChain && capEvent.delegationChain.length > 0) {
        currentId = capEvent.delegationChain[0];
      } else {
        currentId = undefined;
      }
    }

    return chain.length > 1 ? chain : undefined;
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// CONSOLE AUDIT SERVICE (for logging to console)
// ═══════════════════════════════════════════════════════════════════════════

/**
 * ConsoleAuditService - Logs audit events to console.
 *
 * Useful for development and debugging.
 * Wraps SecurityAuditService and adds console output.
 */
export class ConsoleAuditService implements IAuditService {
  private readonly _inner: SecurityAuditService;
  private readonly _prefix: string;

  constructor(prefix: string = '[AUDIT]') {
    this._inner = new SecurityAuditService();
    this._prefix = prefix;
  }

  record(input: IAuditRecordInput, level: AuditLevel): string {
    const eventId = this._inner.record(input, level);

    if (eventId) {
      const outcomeSymbol =
        input.outcome === 'success' ? '✓' : input.outcome === 'denied' ? '✗' : '!';
      const msg = `${this._prefix} ${outcomeSymbol} ${input.action} ${input.target} by ${input.principal}`;

      if (input.outcome === 'denied' || input.outcome === 'error') {
        // eslint-disable-next-line no-console -- console audit sink intentionally writes denied events to console
        console.warn(msg, input.denialReason ?? input.errorDetails ?? '');
      } else {
        // eslint-disable-next-line no-console -- console audit sink intentionally writes successful events to console
        console.log(msg);
      }
    }

    return eventId;
  }

  query(query: IAuditQuery): IAuditQueryResult {
    return this._inner.query(query);
  }

  getCapabilityLineage(capabilityId: string): IAuditEvent[] {
    return this._inner.getCapabilityLineage(capabilityId);
  }

  getOutcomeStats(fromTimestamp?: number, toTimestamp?: number) {
    return this._inner.getOutcomeStats(fromTimestamp, toTimestamp);
  }

  export(query: IAuditQuery): IAuditEvent[] | IAuditEventSummary[] {
    return this._inner.export(query);
  }

  clearOlderThan(olderThan: number): number {
    return this._inner.clearOlderThan(olderThan);
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// NULL AUDIT SERVICE (for testing/when audit disabled)
// ═══════════════════════════════════════════════════════════════════════════

/**
 * NullAuditService - No-op implementation.
 *
 * For tests or when auditing is completely disabled.
 */
export class NullAuditService implements IAuditService {
  record(_input: IAuditRecordInput, _level: AuditLevel): string {
    return '';
  }

  query(_query: IAuditQuery): IAuditQueryResult {
    return { events: [], totalCount: 0, hasMore: false };
  }

  getCapabilityLineage(_capabilityId: string): IAuditEvent[] {
    return [];
  }

  getOutcomeStats(_fromTimestamp?: number, _toTimestamp?: number) {
    return { success: 0, denied: 0, error: 0 };
  }

  export(_query: IAuditQuery): IAuditEvent[] | IAuditEventSummary[] {
    return [];
  }

  clearOlderThan(_olderThan: number): number {
    return 0;
  }
}
