// lib/editor/persistence/save_scheduler.dart
//
// Debounced save scheduler.  The snapshot/JSON is captured SYNCHRONOUSLY by
// the schedule() call before any await, so callers do not need to worry about
// the in-memory state mutating between schedule() and the actual write.

import 'dart:async';

import '../engine/stroke_model.dart';
import 'editor_repository.dart';

/// Debounced save scheduler that batches rapid successive changes to a host
/// into a single [EditorRepository.saveHost] call.
///
/// Usage:
/// ```dart
/// final scheduler = SaveScheduler(repository);
/// scheduler.schedule('page', hostId, List.from(strokes));
/// // …later, on dispose / navigate away:
/// await scheduler.flush();
/// scheduler.dispose();
/// ```
class SaveScheduler {
  SaveScheduler(
    this._repository, {
    Duration debounce = const Duration(milliseconds: 800),
  }) : _debounce = debounce;

  final EditorRepository _repository;
  final Duration _debounce;

  // One pending timer + captured snapshot per host.
  final Map<String, Timer> _timers = {};
  final Map<String, _PendingWrite> _pending = {};

  bool _disposed = false;

  // ── Public API ─────────────────────────────────────────────────────────

  /// Schedule a save for ([hostKind], [hostId]).
  ///
  /// [strokes] is captured synchronously (defensive copy via the caller's
  /// `List.from(…)` convention or equivalent) so mutations after this call
  /// do not affect what is written.
  void schedule(
    String hostKind,
    String hostId,
    List<EditorStroke> strokes,
  ) {
    if (_disposed) return;

    // Capture the snapshot synchronously before any async gap.
    _pending[hostId] = _PendingWrite(hostKind: hostKind, strokes: strokes);

    _timers[hostId]?.cancel();
    _timers[hostId] = Timer(_debounce, () => _fire(hostId));
  }

  /// Force-write all pending saves immediately and wait for them to complete.
  Future<void> flush() async {
    final hosts = List<String>.from(_pending.keys);
    for (final hostId in hosts) {
      _timers[hostId]?.cancel();
      _timers.remove(hostId);
      await _fire(hostId);
    }
  }

  /// Cancel all pending timers and release resources.
  ///
  /// Call [flush] first if you need pending writes to complete.
  void dispose() {
    _disposed = true;
    for (final timer in _timers.values) {
      timer.cancel();
    }
    _timers.clear();
    _pending.clear();
  }

  // ── Internal ───────────────────────────────────────────────────────────

  Future<void> _fire(String hostId) async {
    final write = _pending.remove(hostId);
    _timers.remove(hostId);
    if (write == null) return;
    await _repository.saveHost(write.hostKind, hostId, write.strokes);
  }
}

// ---------------------------------------------------------------------------

class _PendingWrite {
  const _PendingWrite({required this.hostKind, required this.strokes});

  final String hostKind;
  final List<EditorStroke> strokes;
}
