import 'package:dio/dio.dart';
import 'package:real_estate_mobile/core/network/api_client.dart';
import 'package:real_estate_mobile/core/network/api_exceptions.dart';

class ProfileRepository {
  final Dio _dio = ApiClient.instance.dio;

  /// Fetch profile based on role.
  /// Agent: GET /agents/profile/me
  /// User: GET /users/profile/me
  Future<Map<String, dynamic>> getMyProfile(String role) async {
    try {
      final endpoint = role == 'AGENT'
          ? '/agents/profile/me'
          : '/users/profile/me';
      final response = await _dio.get(endpoint);
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch profile',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Update profile based on role.
  /// Agent: PUT /agents/profile
  /// User: PATCH /users/profile/me
  Future<Map<String, dynamic>> updateProfile(
    String role,
    Map<String, dynamic> data,
  ) async {
    try {
      final Response response;
      if (role == 'AGENT') {
        response = await _dio.put('/agents/profile', data: data);
      } else {
        response = await _dio.patch('/users/profile/me', data: data);
      }
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to update profile',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get presigned upload URL for avatar.
  /// Returns { uploadUrl: String, key: String }
  Future<Map<String, String>> getAvatarUploadUrl(
      String role, String fileName, String contentType) async {
    try {
      final endpoint = role == 'AGENT'
          ? '/upload/avatar-presigned-url'
          : '/upload/user-avatar-presigned-url';
      final response = await _dio.post(endpoint, data: {
        'filename': fileName,
        'contentType': contentType,
      });
      final data = response.data['data'] as Map<String, dynamic>;
      return {
        'uploadUrl': data['uploadUrl'] as String,
        'key': data['key'] as String,
      };
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to get upload URL',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Upload file bytes directly to S3 presigned URL.
  Future<void> uploadToS3(
      String uploadUrl, List<int> fileBytes, String contentType) async {
    try {
      await Dio().put(
        uploadUrl,
        data: Stream.fromIterable(fileBytes.map((e) => [e])),
        options: Options(
          headers: {
            'Content-Type': contentType,
            'Content-Length': fileBytes.length,
          },
        ),
      );
    } on DioException catch (e) {
      throw ApiException(
        message: e.message ?? 'Failed to upload file',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get presigned upload URL for documents.
  /// Returns { uploadUrl: String, key: String, publicUrl: String }
  Future<Map<String, String>> getDocumentUploadUrl(
      String fileName, String contentType) async {
    try {
      final response = await _dio.post('/upload/presigned-url', data: {
        'filename': fileName,
        'contentType': contentType,
      });
      final data = response.data['data'] as Map<String, dynamic>;
      return {
        'uploadUrl': data['uploadUrl'] as String,
        'key': data['key'] as String,
        'publicUrl': (data['publicUrl'] ?? data['key'] ?? '') as String,
      };
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to get document upload URL',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Delete avatar from S3: DELETE /upload/{encodedKey}
  Future<void> deleteAvatar(String key) async {
    try {
      final encodedKey = Uri.encodeComponent(key);
      await _dio.delete('/upload/$encodedKey');
    } on DioException catch (_) {
      // S3 delete failure is non-critical
    }
  }

  /// Fetch privacy preferences.
  /// Agent: GET /agents/preferences/privacy
  /// User: GET /users/preferences/privacy
  Future<Map<String, dynamic>> getPrivacyPreferences(String role) async {
    try {
      final endpoint = role == 'AGENT'
          ? '/agents/preferences/privacy'
          : '/users/preferences/privacy';
      final response = await _dio.get(endpoint);
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch privacy preferences',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Update privacy preferences.
  /// Agent: PUT /agents/preferences/privacy
  /// User: PUT /users/preferences/privacy
  Future<Map<String, dynamic>> updatePrivacyPreferences(
    String role,
    Map<String, dynamic> data,
  ) async {
    try {
      final endpoint = role == 'AGENT'
          ? '/agents/preferences/privacy'
          : '/users/preferences/privacy';
      final response = await _dio.put(endpoint, data: data);
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to update privacy preferences',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Delete account.
  /// Agent: DELETE /agents/account
  /// User: DELETE /users/account
  Future<void> deleteAccount(String role) async {
    try {
      final endpoint =
          role == 'AGENT' ? '/agents/account' : '/users/account';
      await _dio.delete(endpoint);
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to delete account',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Change password: POST /auth/change-password
  Future<void> changePassword(
      String currentPassword, String newPassword) async {
    try {
      await _dio.post('/auth/change-password', data: {
        'currentPassword': currentPassword,
        'newPassword': newPassword,
      });
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.response?.data?['message'] ??
            e.message ??
            'Failed to change password',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Fetch notification preferences.
  /// Agent: GET /agents/preferences/notifications
  /// User: GET /users/preferences/notifications
  Future<Map<String, dynamic>> getNotificationPreferences(String role) async {
    try {
      final endpoint = role == 'AGENT'
          ? '/agents/preferences/notifications'
          : '/users/preferences/notifications';
      final response = await _dio.get(endpoint);
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch notification preferences',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Update notification preferences.
  /// Agent: PUT /agents/preferences/notifications
  /// User: PUT /users/preferences/notifications
  Future<Map<String, dynamic>> updateNotificationPreferences(
    String role,
    Map<String, dynamic> data,
  ) async {
    try {
      final endpoint = role == 'AGENT'
          ? '/agents/preferences/notifications'
          : '/users/preferences/notifications';
      final response = await _dio.put(endpoint, data: data);
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to update notification preferences',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Generate testimonial link: POST /testimonials/generate-link
  Future<String> generateTestimonialLink() async {
    try {
      final response = await _dio.post('/testimonials/generate-link');
      return response.data['data']['token'] as String;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to generate testimonial link',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get agent's testimonials: GET /testimonials/my?sort=latest|oldest
  Future<List<Map<String, dynamic>>> getMyTestimonials(
      {String sort = 'latest'}) async {
    try {
      final response =
          await _dio.get('/testimonials/my', queryParameters: {'sort': sort});
      final list = response.data['data'] as List<dynamic>;
      return list.cast<Map<String, dynamic>>();
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch testimonials',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get current subscription: GET /stripe/subscription
  Future<Map<String, dynamic>?> getSubscription() async {
    try {
      final response = await _dio.get('/stripe/subscription');
      return response.data['data'] as Map<String, dynamic>?;
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) return null;
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch subscription',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Create Stripe checkout session: POST /stripe/create-checkout-session
  Future<String> createCheckoutSession(String planId) async {
    try {
      final response = await _dio.post('/stripe/create-checkout-session',
          data: {'planId': planId});
      return response.data['data']['url'] as String;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to create checkout session',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Create Stripe billing portal: POST /stripe/create-portal-session
  Future<String> createPortalSession() async {
    try {
      final response = await _dio.post('/stripe/create-portal-session');
      return response.data['data']['url'] as String;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to create billing portal',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get sections for an agent type: GET /profile-sections/agent-type/{agentTypeId}
  /// Returns { agentType: {...}, sections: [...] }
  Future<Map<String, dynamic>> getSectionsForAgentType(
      String agentTypeId) async {
    try {
      final response =
          await _dio.get('/profile-sections/agent-type/$agentTypeId');
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch profile sections',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get field values: GET /agents/profile/field-values
  /// Returns { agentProfileId: "...", fieldValues: [...] }
  Future<Map<String, dynamic>> getFieldValues() async {
    try {
      final response = await _dio.get('/agents/profile/field-values');
      return response.data['data'] as Map<String, dynamic>;
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch field values',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Save field values: PUT /agents/profile/field-values
  Future<void> saveFieldValues(List<Map<String, dynamic>> fieldValues) async {
    try {
      await _dio.put('/agents/profile/field-values', data: {
        'fieldValues': fieldValues,
      });
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to save field values',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Cancel subscription: POST /stripe/cancel-subscription
  Future<void> cancelSubscription() async {
    try {
      await _dio.post('/stripe/cancel-subscription');
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to cancel subscription',
        statusCode: e.response?.statusCode,
      );
    }
  }

  /// Get available plans: GET /stripe/plans
  Future<List<Map<String, dynamic>>> getStripePlans() async {
    try {
      final response = await _dio.get('/stripe/plans');
      final list = response.data['data'] as List<dynamic>;
      return list.cast<Map<String, dynamic>>();
    } on DioException catch (e) {
      if (e.error is ApiException) throw e.error as ApiException;
      throw ApiException(
        message: e.message ?? 'Failed to fetch plans',
        statusCode: e.response?.statusCode,
      );
    }
  }
}
