import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../library/library_providers.dart';
import '../models/home_index.dart';

/// Pre-warms the drift cache for likely-tap targets. Conservative on
/// purpose: only warms artistProvider rows (single row, single round
/// trip per id) and only ever fires once per id per session. Album
/// detail is NOT prewarmed — the albumProvider auto-fetches its track
/// list when missing, and pre-warming N albums fans out N parallel
/// "fetch tracks" round trips that compete with the user's actual
/// playback request for bandwidth.
///
/// Driven off the per-item home path (homeIndexProvider). Only the
/// artist-typed sections (rediscover / last-played artists) carry
/// artist ids directly; album/track tiles hydrate their own artist on
/// render via the per-item path, so warming those here is unnecessary.
class MetadataPrefetcher {
  MetadataPrefetcher(this._ref) {
    _ref.listen<AsyncValue<HomeIndex>>(homeIndexProvider, (_, next) {
      next.whenData(_warmIndex);
    });
  }

  final Ref _ref;

  /// Per-session dedupe so re-rendering a screen (every UI rebuild
  /// fires the data: callback) doesn't trigger N more fetches for
  /// ids we've already warmed.
  final Set<String> _warmedArtists = {};

  /// Cap warmed per emission. Covers what fits on screen without
  /// scrolling.
  static const _topN = 8;

  /// Pre-warm artists. Albums are intentionally not pre-warmed —
  /// see class comment.
  void warmArtists(Iterable<String> ids) {
    var n = 0;
    for (final id in ids) {
      if (id.isEmpty) continue;
      if (!_warmedArtists.add(id)) continue; // already warmed
      if (n++ >= _topN) break;
      _swallow(_ref.read(artistProvider(id).future));
    }
  }

  void _warmIndex(HomeIndex h) {
    warmArtists(<String>{
      ...h.rediscoverArtists.take(_topN),
      ...h.lastPlayedArtists.take(_topN),
    });
  }

  /// Discards the return value and any error from a fire-and-forget
  /// provider read. We don't care about the value here — we only want
  /// the side effect of writing drift.
  void _swallow(Future<Object?> f) {
    f.then<void>((_) {}).onError((_, __) {});
  }
}

/// Read once at app start to activate the prefetcher (e.g. wire it
/// from a top-level Consumer or main.dart container override).
final metadataPrefetcherProvider = Provider<MetadataPrefetcher>((ref) {
  return MetadataPrefetcher(ref);
});
