import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../data/content_repository.dart';
import '../data/save_repository.dart';
import '../data/streak.dart';
import '../game/models/season.dart';
import '../services/ad_service.dart';
import '../services/analytics_service.dart';
import '../services/audio_service.dart';
import '../services/consent_service.dart';
import '../services/iap_service.dart';
import '../services/music_service.dart';
import '../services/review_service.dart';
import '../services/store_reviewer.dart';
import 'ads_notifier.dart';
import 'endless_best_notifier.dart';
import 'music_notifier.dart';
import 'sound_notifier.dart';
import 'game_session_notifier.dart';
import 'progress_notifier.dart';
import 'season_flow_notifier.dart';
import 'streak_notifier.dart';
import 'tutorial_notifier.dart';

final gameSessionProvider =
    NotifierProvider<GameSessionNotifier, GameViewState?>(
  GameSessionNotifier.new,
);

final soundEnabledProvider =
    NotifierProvider<SoundEnabledNotifier, bool>(SoundEnabledNotifier.new);

final audioServiceProvider = Provider<AudioService>((ref) {
  final service = AudioService(enabled: ref.read(soundEnabledProvider));
  ref.listen<bool>(soundEnabledProvider, (_, next) => service.enabled = next);
  ref.onDispose(service.dispose);
  return service;
});

final musicEnabledProvider =
    NotifierProvider<MusicEnabledNotifier, bool>(MusicEnabledNotifier.new);

final musicServiceProvider = Provider<MusicService>((ref) {
  final service = MusicService(enabled: ref.read(musicEnabledProvider));
  ref.listen<bool>(musicEnabledProvider, (_, next) => service.enabled = next);
  ref.onDispose(service.dispose);
  return service;
});

/// Overridden with the opened repository in main() (and in tests).
final saveRepositoryProvider = Provider<SaveRepository>(
  (ref) => throw UnimplementedError('override with an opened SaveRepository'),
);

final progressProvider =
    NotifierProvider<ProgressNotifier, Map<String, StageProgress>>(
  ProgressNotifier.new,
);

final seasonFlowProvider = NotifierProvider<SeasonFlowNotifier, SeasonFlow?>(
  SeasonFlowNotifier.new,
);

final contentRepositoryProvider =
    Provider<ContentRepository>((ref) => ContentRepository());

final seasonsProvider = FutureProvider<List<SeasonPack>>((ref) {
  // Watching (not awaiting) the one-shot sync makes this provider re-run
  // once when the sync completes, picking up freshly cached packs. Local
  // content loads immediately; the network never blocks this future.
  ref.watch(seasonRefreshProvider);
  return ref.read(contentRepositoryProvider).availableSeasons();
});

/// One background content sync per app session. Home listens and refreshes
/// the season list when new packs arrived.
final seasonRefreshProvider = FutureProvider<bool>(
  (ref) => ref.read(contentRepositoryProvider).refresh(),
);

/// The season players land in by default: Season 1, the start of the journey,
/// so a new player experiences the content in order rather than being dropped
/// into the newest season. (availableSeasons is sorted by seasonId ascending,
/// so `.first` is Season 1.) A season selector to reach later seasons is a
/// planned follow-up.
SeasonPack activeSeason(List<SeasonPack> seasons) => seasons.first;

final streakProvider = NotifierProvider<StreakNotifier, StreakState>(
  StreakNotifier.new,
);

final tutorialProvider = NotifierProvider<TutorialNotifier, TutorialStep?>(
  TutorialNotifier.new,
);

final endlessBestProvider = NotifierProvider<EndlessBestNotifier, int>(
  EndlessBestNotifier.new,
);

final adsRemovedProvider =
    NotifierProvider<AdsRemovedNotifier, bool>(AdsRemovedNotifier.new);

/// Reads ownership live from [adsRemovedProvider]; a mid-session purchase
/// takes effect on the next ad decision without re-wiring.
final adServiceProvider = Provider<AdService>((ref) {
  final service = AdService(adsRemoved: () => ref.read(adsRemovedProvider));
  ref.onDispose(service.dispose);
  return service;
});

final consentServiceProvider = Provider<ConsentService>(
  (ref) => ConsentService(ref.read(adServiceProvider)),
);

/// A verified remove_ads purchase/restore grants the entitlement through the
/// notifier (persists + flips state), which AdService and the banner observe.
final iapServiceProvider = Provider<IapService>((ref) {
  final service = IapService(
    onEntitlementGranted: () =>
        ref.read(adsRemovedProvider.notifier).grant(),
  );
  ref.onDispose(service.dispose);
  service.initialize();
  return service;
});

final analyticsProvider = Provider<AnalyticsService>(
  (ref) => AnalyticsService(DebugAnalyticsBackend()),
);

/// Asks for a store review at most once, after a genuine high point. Reads the
/// one-time flag and cleared-stage count live from [saveRepositoryProvider].
final reviewServiceProvider = Provider<ReviewService>(
  (ref) => ReviewService(
    save: ref.read(saveRepositoryProvider),
    reviewer: StoreReviewer(),
  ),
);

/// The visual theme of whatever season is in play; fallback outside seasons
/// (home, endless). Pure model — UI converts via ThemeColors.
final activeThemeProvider = Provider<SeasonTheme>((ref) {
  final flow = ref.watch(seasonFlowProvider);
  return flow?.pack.theme ?? SeasonTheme.fallback;
});
