import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

/// Application-wide logger with configurable levels and output
class AppLogger {
  static final AppLogger _instance = AppLogger._internal();
  factory AppLogger() => _instance;

  late final Logger _logger;
  bool _isInitialized = false;

  AppLogger._internal();

  /// Initialize the logger with appropriate settings
  void initialize({Level? level, LogOutput? output, LogFilter? filter}) {
    if (_isInitialized) return;

    _logger = Logger(
      level: level ?? _getDefaultLevel(),
      filter: filter ?? ProductionFilter(),
      printer: _createPrinter(),
      output: output ?? _createOutput(),
    );

    _isInitialized = true;
  }

  /// Get default log level based on build mode
  Level _getDefaultLevel() {
    if (kDebugMode) {
      return Level.debug;
    } else if (kProfileMode) {
      return Level.info;
    } else {
      return Level.warning;
    }
  }

  /// Create appropriate printer based on build mode
  LogPrinter _createPrinter() {
    if (kDebugMode) {
      // Detailed printer for development
      return PrettyPrinter(
        methodCount: 2,
        errorMethodCount: 8,
        lineLength: 120,
        colors: true,
        printEmojis: true,
        dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart,
      );
    } else {
      // Simple printer for production
      return SimplePrinter(colors: false);
    }
  }

  /// Create appropriate output based on build mode
  LogOutput _createOutput() {
    if (kReleaseMode) {
      // In production, you might want to send logs to a file or remote service
      return ConsoleOutput();
    } else {
      return ConsoleOutput();
    }
  }

  /// Ensure logger is initialized
  void _ensureInitialized() {
    if (!_isInitialized) {
      initialize();
    }
  }

  /// Log a trace message (most verbose)
  void trace(String message, {Object? error, StackTrace? stackTrace}) {
    _ensureInitialized();
    _logger.t(message, error: error, stackTrace: stackTrace);
  }

  /// Log a debug message
  void debug(String message, {Object? error, StackTrace? stackTrace}) {
    _ensureInitialized();
    _logger.d(message, error: error, stackTrace: stackTrace);
  }

  /// Log an info message
  void info(String message, {Object? error, StackTrace? stackTrace}) {
    _ensureInitialized();
    _logger.i(message, error: error, stackTrace: stackTrace);
  }

  /// Log a warning message
  void warning(String message, {Object? error, StackTrace? stackTrace}) {
    _ensureInitialized();
    _logger.w(message, error: error, stackTrace: stackTrace);
  }

  /// Log an error message
  void error(String message, {Object? error, StackTrace? stackTrace}) {
    _ensureInitialized();
    _logger.e(message, error: error, stackTrace: stackTrace);
  }

  /// Log a fatal error message
  void fatal(String message, {Object? error, StackTrace? stackTrace}) {
    _ensureInitialized();
    _logger.f(message, error: error, stackTrace: stackTrace);
  }

  /// Log network request
  void logRequest(
    String method,
    String url, {
    Map<String, dynamic>? headers,
    dynamic body,
  }) {
    if (!kReleaseMode) {
      _ensureInitialized();
      final buffer = StringBuffer();
      buffer.writeln('→ $method $url');

      if (headers != null && headers.isNotEmpty) {
        buffer.writeln('Headers: ${_sanitizeHeaders(headers)}');
      }

      if (body != null) {
        buffer.writeln('Body: ${_sanitizeBody(body)}');
      }

      _logger.d(buffer.toString());
    }
  }

  /// Log network response
  void logResponse(
    String method,
    String url,
    int statusCode, {
    dynamic body,
    Duration? duration,
  }) {
    if (!kReleaseMode) {
      _ensureInitialized();
      final buffer = StringBuffer();
      buffer.write('← $method $url [$statusCode]');

      if (duration != null) {
        buffer.write(' (${duration.inMilliseconds}ms)');
      }
      buffer.writeln();

      if (body != null) {
        buffer.writeln('Body: ${_sanitizeBody(body)}');
      }

      if (statusCode >= 200 && statusCode < 300) {
        _logger.d(buffer.toString());
      } else if (statusCode >= 400) {
        _logger.e(buffer.toString());
      } else {
        _logger.w(buffer.toString());
      }
    }
  }

  /// Sanitize headers to remove sensitive information
  Map<String, dynamic> _sanitizeHeaders(Map<String, dynamic> headers) {
    final sanitized = Map<String, dynamic>.from(headers);

    // List of sensitive header keys to redact
    const sensitiveKeys = [
      'authorization',
      'cookie',
      'set-cookie',
      'x-api-key',
      'x-auth-token',
    ];

    for (final key in sensitiveKeys) {
      if (sanitized.containsKey(key.toLowerCase())) {
        sanitized[key] = '***REDACTED***';
      }
    }

    return sanitized;
  }

  /// Sanitize body to remove sensitive information
  dynamic _sanitizeBody(dynamic body) {
    if (body is Map) {
      final sanitized = Map<String, dynamic>.from(body);

      // List of sensitive field names to redact
      const sensitiveFields = [
        'password',
        'pwd',
        'passwd',
        'token',
        'secret',
        'apiKey',
        'api_key',
      ];

      for (final field in sensitiveFields) {
        if (sanitized.containsKey(field)) {
          sanitized[field] = '***REDACTED***';
        }
      }

      return sanitized;
    }

    // For string bodies, check if it contains sensitive patterns
    if (body is String) {
      if (body.length > 1000) {
        return '${body.substring(0, 1000)}... (truncated)';
      }

      // Redact potential passwords in query strings or form data
      return body.replaceAllMapped(
        RegExp(
          r'(password|pwd|passwd|token|secret)=[^&\s]+',
          caseSensitive: false,
        ),
        (match) => '${match.group(1)}=***REDACTED***',
      );
    }

    return body;
  }

  /// Close the logger and release resources
  void close() {
    if (_isInitialized) {
      _logger.close();
      _isInitialized = false;
    }
  }
}
