import 'dart:async';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

import '../models/profile.dart';
import 'auth_provider.dart';
import 'supabase_provider.dart';
import 'stream_recovery.dart';

final currentUserIdProvider = Provider<String?>((ref) {
  final authState = ref.watch(authStateChangesProvider);
  // Be explicit about loading/error to avoid dynamic dispatch problems.
  return authState.when(
    data: (state) => state.session?.user.id,
    loading: () => ref.watch(sessionProvider)?.user.id,
    error: (error, _) => ref.watch(sessionProvider)?.user.id,
  );
});

final currentProfileProvider = StreamProvider<Profile?>((ref) {
  final userId = ref.watch(currentUserIdProvider);
  if (userId == null) {
    return const Stream.empty();
  }
  final client = ref.watch(supabaseClientProvider);

  final wrapper = StreamRecoveryWrapper<Profile?>(
    stream: client.from('profiles').stream(primaryKey: ['id']).eq('id', userId),
    onPollData: () async {
      final data = await client
          .from('profiles')
          .select()
          .eq('id', userId)
          .maybeSingle();
      return data == null ? [] : [Profile.fromMap(data)];
    },
    fromMap: Profile.fromMap,
  );

  ref.onDispose(wrapper.dispose);
  return wrapper.stream.map((result) {
    return result.data.isEmpty ? null : result.data.first;
  });
});

final profilesProvider = StreamProvider<List<Profile>>((ref) {
  final client = ref.watch(supabaseClientProvider);

  final wrapper = StreamRecoveryWrapper<Profile>(
    stream: client
        .from('profiles')
        .stream(primaryKey: ['id'])
        .order('full_name'),
    onPollData: () async {
      final data = await client.from('profiles').select().order('full_name');
      return data.map(Profile.fromMap).toList();
    },
    fromMap: Profile.fromMap,
  );

  ref.onDispose(wrapper.dispose);
  return wrapper.stream.map((result) => result.data);
});

/// Controller for the current user's profile (update full name / password).
final profileControllerProvider = Provider<ProfileController>((ref) {
  final client = ref.watch(supabaseClientProvider);
  return ProfileController(client);
});

class ProfileController {
  ProfileController(this._client);

  final SupabaseClient _client;

  /// Update the `profiles.full_name` for the given user id.
  Future<void> updateFullName({
    required String userId,
    required String fullName,
  }) async {
    await _client
        .from('profiles')
        .update({'full_name': fullName})
        .eq('id', userId);
  }

  /// Update the current user's password (works for OAuth users too).
  Future<void> updatePassword(String password) async {
    if (password.length < 8) {
      throw Exception('Password must be at least 8 characters');
    }
    await _client.auth.updateUser(UserAttributes(password: password));
  }
}

final isAdminProvider = Provider<bool>((ref) {
  final profileAsync = ref.watch(currentProfileProvider);
  return profileAsync.maybeWhen(
    data: (profile) => profile?.role == 'admin',
    orElse: () => false,
  );
});
