import 'dart:io';

import 'auto_backup_store.dart';
import 'export_import_service.dart';

/// Makes a silent, encrypted safety copy of the inventory on a schedule — a net
/// against in-app data loss or DB corruption, with zero friction. Triggered
/// from the app lifecycle (see [AutoBackupGate]), it writes into the app's
/// private storage; the manual "save a copy" stays the way to move one off the
/// device.
///
/// Copies are **sealed** (same [ExportImportService.buildSealedBackup] bytes as
/// a manual backup): they reveal nothing at rest and open with the recovery
/// code. The newest few are kept and older ones rotated out.
class AutoBackupService {
  AutoBackupService({
    required ExportImportService exporter,
    required AutoBackupStore store,
    required Future<Directory> Function() directory,
    DateTime Function() now = DateTime.now,
  }) : _exporter = exporter,
       _store = store,
       _directory = directory,
       _now = now;

  final ExportImportService _exporter;
  final AutoBackupStore _store;
  final Future<Directory> Function() _directory;
  final DateTime Function() _now;

  /// Filename prefix so rotation only ever touches automatic copies, never a
  /// backup the user saved by hand.
  static const _prefix = 'tanemaki-auto-';

  /// How many automatic copies to keep. A rotating window (not a single file)
  /// so a corrupt copy can't overwrite the only backup.
  static const _keep = 3;

  /// When the last automatic copy was made, or null if none has run yet. For a
  /// reassurance line in Settings.
  Future<DateTime?> lastBackupAt() => _store.lastBackupAt();

  /// Makes a fresh copy when at least [interval] has passed since the last one.
  /// Returns true when it wrote a copy, false when not due (or on any failure —
  /// a backup must never crash startup or the move to the background).
  Future<bool> runIfDue({Duration interval = const Duration(days: 7)}) async {
    try {
      final last = await _store.lastBackupAt();
      if (last != null && _now().difference(last) < interval) return false;
    } on Object {
      return false;
    }
    return backupNow();
  }

  /// Makes a copy right now, ignoring the schedule — for an explicit "back up
  /// now" tap. Returns true on success, false on any failure (never throws).
  Future<bool> backupNow() async {
    try {
      final now = _now();
      final dir = await _directory();
      await dir.create(recursive: true);
      final sealed = await _exporter.buildSealedBackup();
      final date = now.toIso8601String().substring(0, 10);
      final file = File('${dir.path}/$_prefix$date.tanemaki');
      await file.writeAsBytes(sealed, flush: true);

      await _rotate(dir);
      await _store.markBackupAt(now);
      return true;
    } on Object {
      // Silent net: a failed copy is logged nowhere sensitive and never thrown.
      return false;
    }
  }

  /// Keeps the newest [_keep] automatic copies; deletes the rest. Filenames end
  /// in an ISO date, so a lexical sort is chronological.
  Future<void> _rotate(Directory dir) async {
    final copies =
        (await dir.list().toList())
            .whereType<File>()
            .where((f) => f.uri.pathSegments.last.startsWith(_prefix))
            .toList()
          ..sort((a, b) => a.path.compareTo(b.path));
    final excess = copies.length - _keep;
    if (excess <= 0) return;
    for (final stale in copies.take(excess)) {
      await stale.delete();
    }
  }
}
