// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only

/// Provides optional FocusFlow runtime file logging.
library;

import 'dart:io';

import 'package:scheduler_core/scheduler_core.dart' as scheduler_core;

import 'focus_flow_runtime_config.dart';

/// Minimal append-only file logger for FocusFlow runtime diagnostics.
final class FocusFlowFileLogger {
  /// Creates a logger with explicit runtime state.
  FocusFlowFileLogger._({
    required this.minimumLevel,
    required this.logFilePath,
    required this._logger,
  });

  /// Additional caller-frame marker for this app wrapper.
  static const List<String> _callerFrameIgnores = <String>[
    'focus_flow_file_logger.dart',
  ];

  /// Creates a disabled logger that drops every message.
  factory FocusFlowFileLogger.disabled({DateTime Function()? now}) {
    scheduler_core.logger.disable(now: now);
    return FocusFlowFileLogger._(
      minimumLevel: null,
      logFilePath: null,
      logger: scheduler_core.FocusFlowLogger.disabled(now: now),
    );
  }

  /// Opens a logger from [config] when both logging settings are present.
  static Future<FocusFlowFileLogger> open(
    FocusFlowRuntimeConfig config, {
    DateTime Function()? now,
    String fileName = 'focus_flow.log',
  }) async {
    final level = config.logLevel;
    final location = config.logFileLocation;
    if (level == null || location == null) {
      return FocusFlowFileLogger.disabled(now: now);
    }

    try {
      final directory = Directory(location);
      await directory.create(recursive: true);
      final logFilePath = _joinPath(directory.path, fileName);
      final resolvedNow = now ?? _utcNow;
      final sink = _fileSink(logFilePath);
      scheduler_core.logger.configure(
        minimumLevel: level,
        sink: sink,
        now: resolvedNow,
        callerFrameIgnores: _callerFrameIgnores,
      );
      scheduler_core.logger.info(
        () =>
            'FocusFlow file logging configured. level=${level.name} path=$logFilePath',
      );
      return FocusFlowFileLogger._(
        minimumLevel: level,
        logFilePath: logFilePath,
        logger: scheduler_core.FocusFlowLogger(
          minimumLevel: level,
          sink: sink,
          now: resolvedNow,
          callerFrameIgnores: _callerFrameIgnores,
        ),
      );
    } on Object catch (error, stackTrace) {
      scheduler_core.logger.error(
        () => 'FocusFlow file logging setup failed; disabling file logging.',
        error: error,
        stackTrace: stackTrace,
      );
      return FocusFlowFileLogger.disabled(now: now);
    }
  }

  /// Minimum severity that will be written, or null when disabled.
  final FocusFlowLogLevel? minimumLevel;

  /// Concrete file path receiving appended log lines.
  final String? logFilePath;

  /// Shared logger implementation that owns gating and caller capture.
  final scheduler_core.FocusFlowLogger _logger;

  /// Whether this logger currently writes to a file.
  bool get isEnabled => logFilePath != null && _logger.isEnabled;

  /// Writes a finest-level [message] when enabled for finest logging.
  Future<void> finest(Object? message) {
    return write(scheduler_core.FocusFlowLogLevel.finest, message);
  }

  /// Writes a finer-level [message] when enabled for finer logging.
  Future<void> finer(Object? message) {
    return write(scheduler_core.FocusFlowLogLevel.finer, message);
  }

  /// Writes a fine-level [message] when enabled for fine logging.
  Future<void> fine(Object? message) {
    return write(scheduler_core.FocusFlowLogLevel.fine, message);
  }

  /// Writes a debug-level [message] when enabled for debug or lower.
  Future<void> debug(Object? message) {
    return write(scheduler_core.FocusFlowLogLevel.debug, message);
  }

  /// Writes an info-level [message] when enabled for info or lower.
  Future<void> info(Object? message) {
    return write(scheduler_core.FocusFlowLogLevel.info, message);
  }

  /// Writes a warning-level [message] when enabled for warning or lower.
  Future<void> warn(Object? message, {Object? error, StackTrace? stackTrace}) {
    return write(
      scheduler_core.FocusFlowLogLevel.warn,
      message,
      error: error,
      stackTrace: stackTrace,
    );
  }

  /// Writes an error-level [message] when file logging is enabled.
  Future<void> error(Object? message, {Object? error, StackTrace? stackTrace}) {
    return write(
      scheduler_core.FocusFlowLogLevel.error,
      message,
      error: error,
      stackTrace: stackTrace,
    );
  }

  /// Writes [message] at [level] when it passes the configured threshold.
  Future<void> write(
    scheduler_core.FocusFlowLogLevel level,
    Object? message, {
    Object? error,
    StackTrace? stackTrace,
  }) {
    _logger.write(level, message, error: error, stackTrace: stackTrace);
    return Future<void>.value();
  }
}

/// Returns the current UTC timestamp.
DateTime _utcNow() => DateTime.now().toUtc();

/// Creates a file sink that appends formatted log entries.
scheduler_core.FocusFlowLogSink _fileSink(String path) {
  return (entry) {
    File(path).writeAsStringSync(
      '${entry.toSingleLine()}\n',
      mode: FileMode.append,
      flush: true,
    );
  };
}

/// Joins [base] and [child] with the platform path separator.
String _joinPath(String base, String child) {
  if (base.endsWith('/') || base.endsWith(r'\')) {
    return '$base$child';
  }
  return '$base${Platform.pathSeparator}$child';
}
