import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:drift/drift.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:slrpg_app/main.dart';

import '../../../../shared/data/local/app_database.dart';
import '../../../../shared/data/remote/api_client.dart';
import '../../../../core/constants/app_constants.dart';

final authRepositoryProvider = Provider<AuthRepository>((ref) {
  final db = ref.watch(appDatabaseProvider);
  final apiClient = ref.watch(apiClientProvider);
  final repo = AuthRepository(db: db, apiClient: apiClient);

  apiClient.authStateChanges.listen((event) {
    if (event.token.isEmpty) {
      repo.clearLocalData();
    }
  });

  return repo;
});

class AuthRepository {
  final AppDatabase db;
  final ApiClient apiClient;
  final _storage = const FlutterSecureStorage();

  Stream<AuthStoreEvent> get authStateChanges => apiClient.authStateChanges;

  AuthRepository({required this.db, required this.apiClient});

  Future<UserCollection> login(String email, String password) async {
    final response = await apiClient.login(email, password);
    if (response.record == null) {
      throw ClientException(
          statusCode: 400,
          response: {'message': 'Login failed: No user record returned'});
    }
    return _saveUserFromApi(response.record!.toJson());
  }

  Future<UserCollection> register({
    required String email,
    required String username,
    required String password,
    required double bodyweight,
    required Map<String, dynamic> inventorySettings,
    Map<String, dynamic>? exerciseVariants,
    Map<String, dynamic>? avatarConfig,
  }) async {
    try {
      final response = await apiClient.register(
        email: email,
        username: username,
        password: password,
        bodyweight: bodyweight,
        inventorySettings: inventorySettings,
        exerciseVariants: exerciseVariants,
        avatarConfig: avatarConfig,
      );

      if (response.record == null) {
        throw ClientException(statusCode: 400, response: {
          'message': 'Registration failed: No user record returned'
        });
      }

      final recordMap = response.record!.toJson();
      var user = await _saveUserFromApi(recordMap);

      if (exerciseVariants != null && exerciseVariants.isNotEmpty) {
        final serverVariants = user.exerciseVariants;
        if (serverVariants == null || serverVariants.isEmpty) {
          final companion = user.toCompanion(true).copyWith(
                exerciseVariants: Value(exerciseVariants),
                isDirty: const Value(true),
                updatedAt: Value(DateTime.now()),
              );
          await db.into(db.users).insertOnConflictUpdate(companion);

          user = (await (db.select(db.users)
                ..where((u) => u.id.equals(user.id)))
              .getSingle());
        }
      }

      return user;
    } catch (e) {
      rethrow;
    }
  }

  Future<void> changePassword(String oldPassword, String newPassword) async {
    final user = await (db.select(db.users)..limit(1)).getSingleOrNull();
    if (user?.serverId != null) {
      await apiClient.updatePassword(
        userId: user!.serverId!,
        oldPassword: oldPassword,
        newPassword: newPassword,
        newPasswordConfirm: newPassword,
      );
    } else {
      throw Exception('User not synced or offline');
    }
  }

  Future<void> deleteAccount() async {
    final user = await (db.select(db.users)..limit(1)).getSingleOrNull();
    if (user?.serverId != null) {
      await apiClient.deleteAccount(user!.serverId!);
    }
    await logout();
  }

  Future<void> logout() async {
    await apiClient.logout();
    await clearLocalData();
  }

  Future<void> clearLocalData() async {
    await _storage.delete(key: AppConstants.keyLastSync);

    await db.transaction(() async {
      await db.delete(db.users).go();
      await db.delete(db.cycles).go();
      await db.delete(db.workouts).go();
      await db.delete(db.quests).go();
    });
  }

  Future<UserCollection> _saveUserFromApi(Map<String, dynamic> record) async {
    await db.delete(db.users).go();

    final companion = UsersCompanion(
      serverId: Value(record['id']),
      email: Value(record['email'] ?? ''),
      xp: Value((record['xp'] as num?)?.toInt() ?? 0),
      level: Value((record['level'] as num?)?.toInt() ?? 1),
      currentBodyweight:
          Value((record['current_bodyweight'] as num?)?.toDouble() ?? 70.0),
      inventorySettings: Value(record['inventory_settings'] ?? {}),
      exerciseVariants: Value(record['exercise_variants'] ?? {}),
      avatarConfig: Value(record['avatar_config'] ?? {}),
      lastSyncAt: Value(DateTime.now()),
      isDirty: const Value(false),
      createdAt: Value(DateTime.now()),
      updatedAt: Value(DateTime.now()),
    );

    final id = await db.into(db.users).insert(companion);
    return (await (db.select(db.users)..where((u) => u.id.equals(id)))
        .getSingle());
  }
}
