/**
 * Secure Logging System
 *
 * Provides environment-aware logging with automatic sanitization
 * Prevents sensitive data exposure in production logs (GDPR/PCI compliance)
 *
 * Sentry Integration:
 * - error() and fatal() automatically call Sentry.captureException() in production
 * - Sentry is entirely optional -- the logger works without it
 */

// Lazy-loaded Sentry module reference (null = not yet attempted, undefined = unavailable)
let _sentry: typeof import("@sentry/nextjs") | null | undefined = null;

/**
 * Returns the Sentry module if available, or undefined.
 * Caches the result after first attempt to avoid repeated dynamic requires.
 */
function getSentry(): typeof import("@sentry/nextjs") | undefined {
  if (_sentry === null) {
    try {
      // Use require so this is resolved at runtime, not bundled
      // eslint-disable-next-line @typescript-eslint/no-require-imports
      _sentry = require("@sentry/nextjs");
    } catch {
      _sentry = undefined;
    }
  }
  return _sentry || undefined;
}

/**
 * Sends an exception to Sentry if available and in production.
 */
function captureToSentry(
  error: Error | unknown,
  message: string,
  meta?: Record<string, unknown>,
): void {
  if (process.env.NODE_ENV !== "production") return;

  const Sentry = getSentry();
  if (!Sentry) return;

  try {
    if (error instanceof Error) {
      Sentry.captureException(error, {
        extra: { message, ...meta },
      });
    } else {
      Sentry.captureMessage(message, {
        level: "error",
        extra: { originalError: String(error), ...meta },
      });
    }
  } catch {
    // Never let Sentry failures break the application
  }
}

interface LogMetadata {
  [key: string]: unknown;
}

export interface StructuredLog {
  timestamp: string;
  level: 'info' | 'warn' | 'error' | 'debug' | 'security' | 'trace' | 'fatal';
  message: string;
  context: LogMetadata;
  service?: string;
  environment?: string;
  requestId?: string;
  correlationId?: string;
  error?: {
    message: string;
    stack?: string;
    name?: string;
  };
}

class Logger {
  private isDevelopment = process.env.NODE_ENV !== 'production';
  private isTest = process.env.NODE_ENV === 'test';

  /**
   * Sanitize message to remove sensitive data
   * Removes: emails, OTP codes, passwords, tokens, API keys, JWTs
   */
  private sanitize(message: string): string {
    if (this.isDevelopment) {
      // Don't sanitize in development for easier debugging
      return message;
    }

    return message
      // Remove email addresses
      .replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[EMAIL_REDACTED]')
      // Remove 6-digit OTP codes
      .replace(/\b\d{6}\b/g, '[OTP_REDACTED]')
      // Remove password values
      .replace(/password[:\s]+\S+/gi, 'password: [REDACTED]')
      // Remove token values
      .replace(/token[:\s]+\S+/gi, 'token: [REDACTED]')
      // Remove JWT tokens
      .replace(/eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+/g, '[JWT_REDACTED]')
      // Remove potential API keys (32+ character alphanumeric strings)
      .replace(/\b[A-Za-z0-9]{32,}\b/g, '[KEY_REDACTED]');
  }

  /**
   * Log informational messages
   */
  info(message: string, meta?: LogMetadata): void {
    const sanitizedMessage = this.sanitize(message);
    if (this.isDevelopment && !this.isTest) {
      console.log(`[INFO] ${message}`, meta || '');
    } else if (!this.isTest) {
      console.log(`[INFO] ${sanitizedMessage}`);
    }
  }

  /**
   * Log warning messages
   */
  warn(message: string, meta?: LogMetadata): void {
    const sanitizedMessage = this.sanitize(message);
    if (this.isDevelopment && !this.isTest) {
      console.warn(`[WARN] ${message}`, meta || '');
    } else if (!this.isTest) {
      console.warn(`[WARN] ${sanitizedMessage}`);
    }
  }

  /**
   * Log error messages (all environments)
   * In production, also reports to Sentry if configured
   */
  error(message: string, error?: Error | unknown, meta?: LogMetadata): void {
    const sanitizedMessage = this.sanitize(message);

    if (this.isDevelopment) {
      console.error(`[ERROR] ${message}`, error, meta || '');
    } else {
      const errorMsg = error instanceof Error ? error.message : String(error);
      console.error(`[ERROR] ${sanitizedMessage}`, this.sanitize(errorMsg));

      // Report to Sentry in production
      captureToSentry(error ?? new Error(message), message, meta as Record<string, unknown>);
    }
  }

  /**
   * Log fatal messages -- always reported to Sentry
   */
  fatal(message: string, error?: Error | unknown, meta?: LogMetadata): void {
    const sanitizedMessage = this.sanitize(message);

    if (this.isDevelopment) {
      console.error(`[FATAL] ${message}`, error, meta || '');
    } else {
      const errorMsg = error instanceof Error ? error.message : String(error);
      console.error(`[FATAL] ${sanitizedMessage}`, this.sanitize(errorMsg));
    }

    // Always report fatal errors to Sentry (even in dev, if Sentry is configured)
    captureToSentry(error ?? new Error(message), message, {
      level: 'fatal',
      ...(meta as Record<string, unknown>),
    });
  }

  /**
   * Log debug messages (development only)
   */
  debug(message: string, meta?: LogMetadata): void {
    if (this.isDevelopment && !this.isTest) {
      console.debug(`[DEBUG] ${message}`, meta || '');
    }
  }

  /**
   * Security event logging (always sanitized)
   */
  security(event: string, details?: unknown): void {
    const sanitizedEvent = this.sanitize(event);
    const timestamp = new Date().toISOString();
    console.warn(
      `[${timestamp}] [SECURITY] ${sanitizedEvent}`,
      this.isDevelopment ? details : undefined
    );
  }

  /**
   * Create a scoped logger with a specific context
   */
  scope(context: string) {
    return {
      info: (message: string, meta?: LogMetadata) =>
        this.info(`[${context}] ${message}`, meta),
      warn: (message: string, meta?: LogMetadata) =>
        this.warn(`[${context}] ${message}`, meta),
      error: (message: string, error?: Error | unknown, meta?: LogMetadata) =>
        this.error(`[${context}] ${message}`, error, meta),
      fatal: (message: string, error?: Error | unknown, meta?: LogMetadata) =>
        this.fatal(`[${context}] ${message}`, error, meta),
      debug: (message: string, meta?: LogMetadata) =>
        this.debug(`[${context}] ${message}`, meta),
      security: (event: string, details?: unknown) =>
        this.security(`[${context}] ${event}`, details),
    };
  }
}

export const logger = new Logger();
export default logger;
