import 'dart:async';

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../../core/api/api_client_provider.dart';
import '../../core/auth/auth_notifier.dart';

part 'usp_seen_provider.g.dart';

const _kPrefsKey = 'usp_seen';

/// One-time gate for the S5b USP onboarding screen (Phase 4, 2026-05-12).
///
/// Local SharedPreferences flag is the runtime source of truth. When an
/// authenticated session is hydrated (bootstrap, OTP verify, social, name
/// patch), the server-side `customers.usp_seen` value is OR-merged into the
/// local flag — true wins. When the user dismisses the USP screen and an
/// account exists, the local true is best-effort propagated to the server via
/// `POST /api/client/auth/usp-seen`.
@Riverpod(keepAlive: true)
class UspSeen extends _$UspSeen {
  @override
  FutureOr<bool> build() async {
    // Watch auth state; whenever an auth-bearing profile arrives, OR-merge the
    // server flag into local. Disposed/recreated automatically with the
    // notifier so no manual cleanup needed.
    ref.listen<AsyncValue<AuthData>>(authProvider, (prev, next) {
      final profile = _profileOf(next.valueOrNull);
      if (profile != null) {
        unawaited(_hydrateFromProfile(profile));
      }
    });

    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool(_kPrefsKey) ?? false;
  }

  Map<String, dynamic>? _profileOf(AuthData? data) => switch (data) {
        AuthAuthenticatedData d => d.profile,
        AuthAnonymousData d => d.profile,
        AuthForceRegisterData d => d.profile,
        AuthNeedsDisplayNameData d => d.profile,
        _ => null,
      };

  Future<void> _hydrateFromProfile(Map<String, dynamic> profile) async {
    final serverSeen = profile['usp_seen'] as bool? ?? false;
    if (!serverSeen) return;
    if ((state.valueOrNull ?? false) == true) return;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(_kPrefsKey, true);
    state = const AsyncData(true);
  }

  /// Mark seen locally; if an account exists, also persist to DB best-effort.
  /// Safe to call when already seen — no-ops out of the network hit if local
  /// is already true AND no account exists yet.
  Future<void> markSeen() async {
    final alreadySeen = (state.valueOrNull ?? false) == true;
    if (!alreadySeen) {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setBool(_kPrefsKey, true);
      state = const AsyncData(true);
    }

    final authData = ref.read(authProvider).valueOrNull;
    final hasAccount = authData is AuthAuthenticatedData ||
        authData is AuthAnonymousData ||
        authData is AuthForceRegisterData ||
        authData is AuthNeedsDisplayNameData;
    if (!hasAccount) return;

    try {
      await ref.read(apiClientProvider).post('/api/client/auth/usp-seen');
    } catch (_) {
      // Local stays true; next markSeen call (or a successful login on a
      // different device) will re-attempt the DB write.
    }
  }
}
