import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logger/logger.dart';
import 'package:pocketbase/pocketbase.dart';

import '../../../core/constants/app_constants.dart';

/// Provider for the [ApiClient] instance.
/// 
/// The provider must be overridden in the [ProviderScope] to provide a concrete implementation.
final apiClientProvider =
    Provider<ApiClient>((ref) => throw UnimplementedError());

/// Client for communicating with the PocketBase backend.
class ApiClient {
  late final PocketBase _pb;
  final Logger _logger;

  /// Underlying PocketBase instance.
  PocketBase get pb => _pb;

  /// Stream of authentication state changes.
  Stream<AuthStoreEvent> get authStateChanges => _pb.authStore.onChange;

  /// Creates a new ApiClient.
  ApiClient({
    required AuthStore authStore,
    Logger? logger,
  }) : _logger = logger ?? Logger() {
    _pb = PocketBase(
      AppConstants.apiBaseUrl,
      authStore: authStore,
    );
  }

  /// Wrapper to handle PocketBase requests with automatic token refreshing.
  Future<T> _handleRequest<T>(Future<T> Function() request) async {
    try {
      return await request();
    } on ClientException catch (e) {
      if (e.statusCode == 401) {
        _logger.w('🔄 Token expired or invalid (401). Attempting refresh...');
        try {
          await _pb.collection('users').authRefresh();
          return await request();
        } catch (refreshError) {
          _logger.e('❌ Token refresh failed - logging out',
              error: refreshError);
          _pb.authStore.clear();
          rethrow;
        }
      }
      _logger.e('API Error: ${e.statusCode} ${e.response}', error: e);
      rethrow;
    } catch (e) {
      _logger.e('Unexpected API Error', error: e);
      rethrow;
    }
  }

  /// Logs in a user using email and password.
  Future<RecordAuth> login(String email, String password) async {
    try {
      final authData = await _pb.collection('users').authWithPassword(
            email,
            password,
          );
      _logger.i('✅ Login successful for ${authData.record.id}');
      return authData;
    } catch (e) {
      _logger.e('Login failed', error: e);
      rethrow;
    }
  }

  /// Registers a new user account.
  Future<RecordAuth> 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 {
      await _pb.collection('users').create(body: {
        'email': email,
        'name': username,
        'password': password,
        'passwordConfirm': password,
        'emailVisibility': true,
        'xp': 0,
        'level': 1,
        'current_bodyweight': bodyweight,
        'inventory_settings': inventorySettings,
        'exercise_variants': exerciseVariants ?? {},
        'avatar_config': avatarConfig ?? {},
      });

      return await login(email, password);
    } catch (e) {
      _logger.e('Registration failed', error: e);
      rethrow;
    }
  }

  /// Requests a verification email for the given address.
  Future<void> requestVerification(String email) async {
    try {
      await _pb.collection('users').requestVerification(email);
      _logger.i('Verification email requested for $email');
    } catch (e) {
      _logger.e('Request verification failed', error: e);
      rethrow;
    }
  }

  /// Forces a token refresh.
  Future<void> refreshAuth() async {
    try {
      await _pb.collection('users').authRefresh();
      _logger.i('✅ Auth refreshed successfully');
    } catch (e) {
      _logger.e('Auth refresh failed', error: e);
      _pb.authStore.clear();
      rethrow;
    }
  }

  /// Clears the authentication store.
  Future<void> logout() async {
    _pb.authStore.clear();
  }

  /// Synchronizes local data with the server.
  Future<Map<String, dynamic>> sync({
    required String lastSyncTimestamp,
    required Map<String, dynamic> pushData,
  }) async {
    return _handleRequest(() async {
      final result = await _pb.send(
        ApiEndpoints.sync,
        method: 'POST',
        body: {
          'last_sync_timestamp': lastSyncTimestamp,
          'push_data': pushData,
        },
      );
      return result as Map<String, dynamic>;
    });
  }

  /// Creates a new training cycle on the server.
  Future<Map<String, dynamic>> createCycle(
      Map<String, double> trainingMaxes) async {
    return _handleRequest(() async {
      final result = await _pb.send(
        ApiEndpoints.cycleCreate,
        method: 'POST',
        body: {'training_maxes': trainingMaxes},
      );
      return result as Map<String, dynamic>;
    });
  }

  /// Marks a training cycle as finished on the server.
  Future<Map<String, dynamic>> finishCycle(String cycleId) async {
    return _handleRequest(() async {
      final result = await _pb.send(
        ApiEndpoints.cycleFinish,
        method: 'POST',
        body: {'cycle_id': cycleId},
      );
      return result as Map<String, dynamic>;
    });
  }

  /// Retrieves the current active cycle from the server.
  Future<Map<String, dynamic>> getCurrentCycle() async {
    return _handleRequest(() async {
      final result = await _pb.send(ApiEndpoints.cycleCurrent, method: 'GET');
      return result as Map<String, dynamic>;
    });
  }

  /// Fetches history statistics for a specific exercise and time range.
  Future<Map<String, dynamic>> getStatsHistory({
    required String exercise,
    required String range,
  }) async {
    return _handleRequest(() async {
      final result = await _pb.send(
        ApiEndpoints.statsHistory,
        method: 'GET',
        query: {
          'exercise': exercise,
          'range': range,
        },
      );
      return result as Map<String, dynamic>;
    });
  }

  /// Retrieves a summary of all statistics.
  Future<Map<String, dynamic>> getStatsSummary() async {
    return _handleRequest(() async {
      final result = await _pb.send(ApiEndpoints.statsSummary, method: 'GET');
      return result as Map<String, dynamic>;
    });
  }

  /// Updates the user's bodyweight on the server.
  Future<void> updateBodyweight(double bodyweight) async {
    await _handleRequest(() async {
      await _pb.send(
        ApiEndpoints.profileBodyweight,
        method: 'PATCH',
        body: {'bodyweight': bodyweight},
      );
    });
  }

  /// Updates the user's inventory settings on the server.
  Future<void> updateInventory(Map<String, dynamic> inventory) async {
    await _handleRequest(() async {
      await _pb.send(
        ApiEndpoints.profileInventory,
        method: 'PATCH',
        body: inventory,
      );
    });
  }

  /// Updates the user's password.
  Future<void> updatePassword({
    required String userId,
    required String oldPassword,
    required String newPassword,
    required String newPasswordConfirm,
  }) async {
    await _handleRequest(() async {
      await _pb.collection('users').update(userId, body: {
        'oldPassword': oldPassword,
        'password': newPassword,
        'passwordConfirm': newPasswordConfirm,
      });
    });
  }

  /// Deletes the user's account.
  Future<void> deleteAccount(String userId) async {
    await _handleRequest(() async {
      await _pb.collection('users').delete(userId);
    });
  }

  /// Resets all training progress on the server.
  Future<void> resetProgress() async {
    await _handleRequest(() async {
      await _pb.send(
        ApiEndpoints.profileReset,
        method: 'POST',
      );
    });
  }

  /// Returns the current authentication token.
  String? getToken() {
    return _pb.authStore.token.isNotEmpty ? _pb.authStore.token : null;
  }

  /// Returns the current user's ID.
  String? getUserId() {
    return _pb.authStore.record?.id;
  }
}
