// Frame / hitch sampler for diagnostic packs.

import 'badnote_log.dart';

class FrameSample {
  FrameSample({
    required this.at,
    required this.label,
    required this.ms,
    this.dropped = false,
  });

  final DateTime at;
  final String label;
  final double ms;
  final bool dropped;

  Map<String, Object?> toJson() => {
        'at': at.toIso8601String(),
        'label': label,
        'ms': ms,
        'dropped': dropped,
      };
}

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

  static const int capacity = 500;
  final List<FrameSample> _samples = <FrameSample>[];
  int overBudget = 0;
  int total = 0;

  /// Budget for a single frame at 60fps.
  static const double budgetMs = 16.6;

  void record(String label, double ms, {bool dropped = false}) {
    total++;
    final over = ms > budgetMs;
    if (over) overBudget++;
    final sample = FrameSample(
      at: DateTime.now(),
      label: label,
      ms: ms,
      dropped: dropped || over,
    );
    _samples.add(sample);
    if (_samples.length > capacity) {
      _samples.removeRange(0, _samples.length - capacity);
    }
    if (over || dropped) {
      BadNoteLog.instance.warn(
        LogSubsystem.frame,
        'slow_frame',
        fields: {'label': label, 'ms': ms, 'dropped': dropped},
      );
    }
  }

  void recordZoom({
    required double rawScale,
    required bool scaleDrop,
    required bool focalDrop,
    required double focalJumpPx,
  }) {
    record(
      'zoom',
      scaleDrop || focalDrop ? budgetMs + 1 : 8,
      dropped: scaleDrop || focalDrop,
    );
    BadNoteLog.instance.debug(
      LogSubsystem.frame,
      'zoom',
      fields: {
        'rawScale': rawScale,
        'scaleDrop': scaleDrop,
        'focalDrop': focalDrop,
        'focalJumpPx': focalJumpPx,
      },
    );
  }

  List<Map<String, Object?>> toJsonList() =>
      _samples.map((s) => s.toJson()).toList(growable: false);

  Map<String, Object?> summary() => {
        'total': total,
        'overBudget': overBudget,
        'budgetMs': budgetMs,
        'recent': toJsonList(),
      };

  void reset() {
    _samples.clear();
    overBudget = 0;
    total = 0;
  }
}
