import 'dart:io';
import 'package:logger/logger.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:on_field_work/helpers/services/api_service.dart';

/// Global logger instance
Logger? _appLogger;
late final FileLogOutput _fileLogOutput;

/// Store logs temporarily for API posting
final List<Map<String, dynamic>> _logBuffer = [];

/// Lock flag to prevent concurrent posting
bool _isPosting = false;

/// Flag to allow API posting only after login
bool _canPostLogs = false;

/// Maximum number of logs before triggering API post
const int _maxLogsBeforePost = 100;

/// Maximum logs in memory buffer
const int _maxBufferSize = 500;

/// Enum → logger level mapping
const _levelMap = {
  LogLevel.debug: Level.debug,
  LogLevel.info: Level.info,
  LogLevel.warning: Level.warning,
  LogLevel.error: Level.error,
  LogLevel.verbose: Level.verbose,
};

/// Initialize logging
Future<void> initLogging() async {
  _fileLogOutput = FileLogOutput();

  _appLogger = Logger(
    printer: PrettyPrinter(
      methodCount: 0,
      printTime: true,
      colors: true,
      printEmojis: true,
    ),
    output: MultiOutput([
      ConsoleOutput(),
      _fileLogOutput,
    ]),
    level: Level.debug,
  );
}

/// Enable API posting after login
void enableRemoteLogging() {
  _canPostLogs = true;
  _postBufferedLogs(); // flush logs if any
}

/// Safe logger wrapper
void logSafe(
  String message, {
  LogLevel level = LogLevel.info,
  dynamic error,
  StackTrace? stackTrace,
  bool sensitive = false,
}) {
  if (sensitive || _appLogger == null) return;

  final loggerLevel = _levelMap[level] ?? Level.info;
  _appLogger!.log(loggerLevel, message, error: error, stackTrace: stackTrace);

  // Buffer logs for API posting
  _logBuffer.add({
    "logLevel": level.name,
    "message": message,
    "timeStamp": DateTime.now().toUtc().toIso8601String(),
    "ipAddress": "this is test IP", // TODO: real IP
    "userAgent": "FlutterApp/1.0", // TODO: device_info_plus
    "details": error?.toString() ?? stackTrace?.toString(),
  });

  if (_logBuffer.length >= _maxLogsBeforePost) {
    _postBufferedLogs();
  }
}

/// Post buffered logs to API
Future<void> _postBufferedLogs() async {
  if (!_canPostLogs) return; // 🚫 skip if not logged in
  if (_isPosting || _logBuffer.isEmpty) return;

  _isPosting = true;
  final logsToSend = List<Map<String, dynamic>>.from(_logBuffer);
  _logBuffer.clear();

  try {
    final success = await ApiService.postLogsApi(logsToSend);
    if (!success) {
      _reinsertLogs(logsToSend, reason: "API call returned false");
    }
  } catch (e) {
    _reinsertLogs(logsToSend, reason: "API exception: $e");
  } finally {
    _isPosting = false;
  }
}

/// Reinsert logs into buffer if posting fails
void _reinsertLogs(List<Map<String, dynamic>> logs, {required String reason}) {
  _appLogger?.w("Failed to post logs, re-queuing. Reason: $reason");

  if (_logBuffer.length + logs.length > _maxBufferSize) {
    _appLogger?.e("Buffer full. Dropping ${logs.length} logs to prevent crash.");
    return;
  }

  _logBuffer.insertAll(0, logs);
}

/// File-based log output (safe storage)
class FileLogOutput extends LogOutput {
  File? _logFile;

  Future<void> _init() async {
    if (_logFile != null) return;

    final baseDir = await getExternalStorageDirectory();
    final directory = Directory('${baseDir!.path}/marco_logs');
    if (!await directory.exists()) {
      await directory.create(recursive: true);
    }

    final date = DateFormat('yyyy-MM-dd').format(DateTime.now());
    final filePath = '${directory.path}/log_$date.txt';
    _logFile = File(filePath);

    if (!await _logFile!.exists()) {
      await _logFile!.create();
    }

    await _cleanOldLogs(directory);
  }

  @override
  void output(OutputEvent event) async {
    await _init();
    if (event.lines.isEmpty) return;

    final logMessage = event.lines.join('\n') + '\n';
    await _logFile!.writeAsString(
      logMessage,
      mode: FileMode.append,
      flush: true,
    );
  }

  Future<String> getLogFilePath() async {
    await _init();
    return _logFile!.path;
  }

  Future<void> clearLogs() async {
    await _init();
    await _logFile!.writeAsString('');
  }

  Future<String> readLogs() async {
    await _init();
    return _logFile!.readAsString();
  }

  Future<void> _cleanOldLogs(Directory directory) async {
    final files = directory.listSync();
    final now = DateTime.now();

    for (var file in files) {
      if (file is File && file.path.endsWith('.txt')) {
        final stat = await file.stat();
        if (now.difference(stat.modified).inDays > 3) {
          await file.delete();
        }
      }
    }
  }
}

/// Custom log levels
enum LogLevel { debug, info, warning, error, verbose }
