class _CacheEntry<T> {
  final T value;
  final DateTime expiresAt;
  final int size;

  _CacheEntry(
      {required this.value, required this.expiresAt, required this.size});

  bool get isExpired => DateTime.now().isAfter(expiresAt);
}

/// 간단한 메모리 기반 캐시 매니저 (TTL/최대 개수/용량 제한)
class SimpleCacheManager<T> {
  final int maxEntries;
  final int maxBytes;
  final Duration ttl;

  final Map<String, _CacheEntry<T>> _store = <String, _CacheEntry<T>>{};
  int _currentBytes = 0;

  // 간단한 메트릭
  int _hits = 0;
  int _misses = 0;
  int _puts = 0;
  int _evictions = 0;

  SimpleCacheManager({
    this.maxEntries = 128,
    this.maxBytes = 1024 * 1024, // 1MB
    this.ttl = const Duration(minutes: 30),
  });

  T? get(String key) {
    final entry = _store.remove(key);
    if (entry == null) return null;
    if (entry.isExpired) {
      _currentBytes -= entry.size;
      _misses++;
      return null;
    }
    // LRU 갱신: 재삽입으로 가장 최근으로 이동
    _store[key] = entry;
    _hits++;
    return entry.value;
  }

  void set(String key, T value, {int size = 1, Duration? customTtl}) {
    final expiresAt = DateTime.now().add(customTtl ?? ttl);
    final existing = _store.remove(key);
    if (existing != null) {
      _currentBytes -= existing.size;
    }
    _store[key] = _CacheEntry(value: value, expiresAt: expiresAt, size: size);
    _currentBytes += size;
    _puts++;
    _evictIfNeeded();
  }

  void invalidate(String key) {
    final removed = _store.remove(key);
    if (removed != null) {
      _currentBytes -= removed.size;
    }
  }

  void clear() {
    _store.clear();
    _currentBytes = 0;
  }

  void _evictIfNeeded() {
    // 개수/용량 제한을 넘으면 오래된 것부터 제거
    while (_store.length > maxEntries || _currentBytes > maxBytes) {
      if (_store.isEmpty) break;
      final firstKey = _store.keys.first;
      final removed = _store.remove(firstKey);
      if (removed != null) {
        _currentBytes -= removed.size;
        _evictions++;
      }
    }
  }

  Map<String, num> dumpMetrics() {
    final totalGets = _hits + _misses;
    final hitRate = totalGets == 0 ? 0 : _hits / totalGets;
    return {
      'entries': _store.length,
      'bytes': _currentBytes,
      'hits': _hits,
      'misses': _misses,
      'hitRate': hitRate,
      'puts': _puts,
      'evictions': _evictions,
    };
  }
}
