import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:drift/drift.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

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

final userRepositoryProvider = Provider<UserRepository>((ref) {
  final db = ref.watch(appDatabaseProvider);
  final apiClient = ref.watch(apiClientProvider);
  return UserRepository(db: db, apiClient: apiClient);
});

final apiClientProvider = Provider<ApiClient>((ref) => ApiClient());

class UserRepository {
  final AppDatabase db;
  final ApiClient apiClient;
  final _storage = const FlutterSecureStorage(); // NEU: Instanz für Logout

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

  Future<UserCollection?> getLocalUser() async {
    return await (db.select(db.users)..limit(1)).getSingleOrNull();
  }

  Future<void> saveLocalUser(UserCollection user) async {
    final companion = user.toCompanion(true).copyWith(
          updatedAt: Value(DateTime.now()),
        );
    await db.into(db.users).insertOnConflictUpdate(companion);
  }

  Future<void> updateXP(int xpToAdd) async {
    final user = await getLocalUser();
    if (user != null) {
      final newXp = user.xp + xpToAdd;
      final companion = UsersCompanion(
        xp: Value(newXp),
        isDirty: const Value(true),
        updatedAt: Value(DateTime.now()),
      );
      await (db.update(db.users)..where((u) => u.id.equals(user.id)))
          .write(companion);
    }
  }

  Future<void> updateLevel(int newLevel) async {
    final user = await getLocalUser();
    if (user != null) {
      final companion = UsersCompanion(
        level: Value(newLevel),
        isDirty: const Value(true),
        updatedAt: Value(DateTime.now()),
      );
      await (db.update(db.users)..where((u) => u.id.equals(user.id)))
          .write(companion);
    }
  }

  Future<void> updateBodyweight(double bodyweight) async {
    final user = await getLocalUser();
    if (user != null) {
      final companion = UsersCompanion(
        currentBodyweight: Value(bodyweight),
        isDirty: const Value(true),
        updatedAt: Value(DateTime.now()),
      );
      await (db.update(db.users)..where((u) => u.id.equals(user.id)))
          .write(companion);

      try {
        await apiClient.updateBodyweight(bodyweight);
      } catch (e) {}
    }
  }

  Future<void> updateInventory(Map<String, dynamic> inventory) async {
    final user = await getLocalUser();
    if (user != null) {
      final companion = UsersCompanion(
        inventorySettings: Value(inventory),
        isDirty: const Value(true),
        updatedAt: Value(DateTime.now()),
      );
      await (db.update(db.users)..where((u) => u.id.equals(user.id)))
          .write(companion);

      try {
        await apiClient.updateInventory(inventory);
      } catch (e) {}
    }
  }

  Future<UserCollection> login(String email, String password) async {
    final response = await apiClient.login(email, password);
    return _saveUserFromApi(response['record']);
  }

  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,
      );

      final record = response['record'] ?? response;
      var user = await _saveUserFromApi(record);

      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());
        }
      }

      try {
        await apiClient.login(email, password);
      } catch (e) {}

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

  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());
  }

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

    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<Map<String, dynamic>> getInventorySettingsAsync() async {
    final user = await getLocalUser();
    if (user?.inventorySettings != null) {
      return user!.inventorySettings!;
    }
    return {
      'bar_weight': 20.0,
      'plates': [20, 20, 10, 10, 5, 5, 2.5, 2.5, 1.25, 1.25],
      'bands': [],
    };
  }

  Future<List<double>> getAvailablePlates() async {
    final inventory = await getInventorySettingsAsync();
    final plates = inventory['plates'] as List?;
    return plates?.map((e) => (e as num).toDouble()).toList() ?? [];
  }

  Future<double> getBarWeight() async {
    final inventory = await getInventorySettingsAsync();
    return (inventory['bar_weight'] as num?)?.toDouble() ?? 20.0;
  }

  Future<void> changePassword(String oldPassword, String newPassword) async {
    final user = await getLocalUser();
    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 getLocalUser();
    if (user?.serverId != null) {
      await apiClient.deleteAccount(user!.serverId!);
    }
    await logout();
  }

  Future<void> resetProgress() async {
    final user = await getLocalUser();
    if (user != null) {
      try {
        await apiClient.resetProgress();
      } catch (e) {
        throw Exception(
            "Server connection required to reset progress. Please try again when online.");
      }

      final companion = UsersCompanion(
        xp: const Value(0),
        level: const Value(1),
        isDirty: const Value(false),
        updatedAt: Value(DateTime.now()),
      );
      await (db.update(db.users)..where((u) => u.id.equals(user.id)))
          .write(companion);

      await db.delete(db.cycles).go();
      await db.delete(db.workouts).go();
    }
  }
}
