// lib/editor/input/diagnostic_logger.dart
//
// On-device input diagnostics. When the user manually enables the diagnostic
// (the toolbar toggle), every native pen event (raw button/flag/tilt fields)
// and every zoom frame is emitted through the standard `dart:developer` log
// channel (name 'badnote.input') — capturable via `flutter run`, DevTools, or
// any log tool — AND mirrored to a text file as a fallback for the packaged
// GUI build, which has no attached console. Disabled by default (no overhead).

import 'dart:async';
import 'dart:developer' as developer;
import 'dart:io';

import 'package:path_provider/path_provider.dart';

class DiagnosticLogger {
  DiagnosticLogger._();
  static final DiagnosticLogger instance = DiagnosticLogger._();

  final List<String> _buffer = <String>[];
  File? _file;
  Timer? _timer;
  int _epochMs = 0;

  bool _active = false;
  bool get isActive => _active;

  /// Absolute path of the current log file (shown in the overlay), or null.
  String? path;

  /// Begin a session. Enables the `dart:developer` log channel immediately and
  /// opens the fallback file (best-effort). Safe to call repeatedly.
  Future<void> start() async {
    if (_active) return;
    _active = true; // developer.log works even if the file can't be opened
    _epochMs = DateTime.now().millisecondsSinceEpoch;
    developer.log('--- session start ${DateTime.now().toIso8601String()} ---',
        name: 'badnote.input');
    try {
      Directory dir;
      try {
        dir = await getApplicationDocumentsDirectory();
      } catch (_) {
        dir = await getTemporaryDirectory();
      }
      final file = File('${dir.path}${Platform.pathSeparator}badnote_input_log.txt');
      await file.writeAsString(
        '# BadNote input diagnostic log\n'
        '# started ${DateTime.now().toIso8601String()}\n'
        '# columns: <ms> <kind> <fields...>\n',
        flush: true,
      );
      _file = file;
      path = file.path;
      _buffer.clear();
      _timer = Timer.periodic(const Duration(seconds: 1), (_) => _flush());
    } catch (_) {
      // File is a fallback; never break the app over it.
    }
  }

  /// Emit one diagnostic line through the standard log channel and the file.
  void log(String line) {
    if (!_active) return;
    developer.log(line, name: 'badnote.input');
    if (_file == null) return;
    final t = DateTime.now().millisecondsSinceEpoch - _epochMs;
    _buffer.add('$t $line');
    if (_buffer.length >= 1000) _flush();
  }

  Future<void> _flush() async {
    final file = _file;
    if (file == null || _buffer.isEmpty) return;
    final chunk = '${_buffer.join('\n')}\n';
    _buffer.clear();
    try {
      await file.writeAsString(chunk, mode: FileMode.append, flush: true);
    } catch (_) {}
  }

  /// Flush and stop. The file remains on disk for retrieval.
  Future<void> stop() async {
    if (!_active) return;
    _active = false;
    _timer?.cancel();
    _timer = null;
    await _flush();
  }
}
