import 'dart:async';
import 'package:xp_nix/src/interfaces/i_time_provider.dart';

/// Mock implementation of ITimeProvider for testing with controllable time
class MockTimeProvider implements ITimeProvider {
  DateTime _currentTime = DateTime(2024, 1, 1, 9, 0); // Default to 9 AM on a workday
  final List<_MockTimer> _activeTimers = [];
  
  @override
  DateTime now() => _currentTime;
  
  @override
  Timer periodic(Duration duration, void Function(Timer) callback) {
    final timer = _MockPeriodicTimer(duration, callback);
    _activeTimers.add(timer);
    return timer;
  }
  
  @override
  Timer timer(Duration duration, void Function() callback) {
    final timer = _MockTimer(duration, callback);
    _activeTimers.add(timer);
    return timer;
  }
  
  /// Advance time by the specified duration and trigger any timers
  void advanceTime(Duration duration) {
    _currentTime = _currentTime.add(duration);
    
    // Check and trigger timers
    final timersToTrigger = <_MockTimer>[];
    for (final timer in _activeTimers) {
      if (timer.shouldTrigger(_currentTime)) {
        timersToTrigger.add(timer);
      }
    }
    
    // Trigger timers and handle periodic vs one-time
    for (final timer in timersToTrigger) {
      timer.trigger();
      if (!timer.isActive) {
        _activeTimers.remove(timer);
      }
    }
  }
  
  /// Set the current time to a specific value
  void setTime(DateTime time) {
    _currentTime = time;
  }
  
  /// Fast forward to a specific time, triggering all timers in between
  void fastForwardTo(DateTime targetTime) {
    while (_currentTime.isBefore(targetTime)) {
      final nextTimerTime = _getNextTimerTime();
      if (nextTimerTime != null && nextTimerTime.isBefore(targetTime)) {
        advanceTime(nextTimerTime.difference(_currentTime));
      } else {
        advanceTime(targetTime.difference(_currentTime));
      }
    }
  }
  
  /// Get the next time a timer should fire
  DateTime? _getNextTimerTime() {
    DateTime? nextTime;
    for (final timer in _activeTimers) {
      final timerTime = timer.nextTriggerTime;
      if (timerTime != null && (nextTime == null || timerTime.isBefore(nextTime))) {
        nextTime = timerTime;
      }
    }
    return nextTime;
  }
  
  /// Cancel all active timers
  void cancelAllTimers() {
    for (final timer in _activeTimers) {
      timer.cancel();
    }
    _activeTimers.clear();
  }
}

/// Mock timer implementation
class _MockTimer implements Timer {
  final Duration _duration;
  final void Function() _callback;
  final DateTime _startTime;
  bool _isActive = true;
  
  _MockTimer(this._duration, this._callback) : _startTime = DateTime(2024, 1, 1, 9, 0);
  
  @override
  bool get isActive => _isActive;
  
  @override
  int get tick => 0; // Not used in mock
  
  DateTime? get nextTriggerTime => _isActive ? _startTime.add(_duration) : null;
  
  bool shouldTrigger(DateTime currentTime) {
    return _isActive && currentTime.isAtOrAfter(_startTime.add(_duration));
  }
  
  void trigger() {
    if (_isActive) {
      _callback();
      _isActive = false;
    }
  }
  
  @override
  void cancel() {
    _isActive = false;
  }
}

/// Mock periodic timer implementation
class _MockPeriodicTimer extends _MockTimer {
  final void Function(Timer) _periodicCallback;
  DateTime _nextTriggerTime;
  
  _MockPeriodicTimer(Duration duration, this._periodicCallback) 
      : _nextTriggerTime = DateTime(2024, 1, 1, 9, 0).add(duration),
        super(duration, () {});
  
  @override
  DateTime? get nextTriggerTime => _isActive ? _nextTriggerTime : null;
  
  @override
  bool shouldTrigger(DateTime currentTime) {
    return _isActive && currentTime.isAtOrAfter(_nextTriggerTime);
  }
  
  @override
  void trigger() {
    if (_isActive) {
      _periodicCallback(this);
      _nextTriggerTime = _nextTriggerTime.add(_duration);
    }
  }
}

/// Extension to help with time comparisons
extension DateTimeComparison on DateTime {
  bool isAtOrAfter(DateTime other) => isAfter(other) || isAtSameMomentAs(other);
}
