import 'dart:io';

import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:logger/Logger.dart';
import 'package:sigap/src/cores/services/supabase_service.dart';
import 'package:sigap/src/features/personalization/data/models/models/profile_model.dart';
import 'package:sigap/src/utils/exceptions/exceptions.dart';
import 'package:sigap/src/utils/exceptions/format_exceptions.dart';
import 'package:sigap/src/utils/exceptions/platform_exceptions.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class ProfileRepository extends GetxController {
  static ProfileRepository get instance => Get.find();

  final _supabase = SupabaseService.instance.client;
  final _logger = Get.find<Logger>();

  // Get current user ID
  String? get currentUserId => _supabase.auth.currentUser?.id;

  // Get user profile data
  Future<ProfileModel> getProfileData() async {
    try {
      if (currentUserId == null) {
        throw 'User not authenticated';
      }

      final profileData =
          await _supabase
              .from('profiles')
              .select()
              .eq('user_id', currentUserId!)
              .single();

      return ProfileModel.fromJson(profileData);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getProfileData: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } on FormatException catch (_) {
      throw const TFormatException();
    } on PlatformException catch (e) {
      throw TPlatformException(e.code).message;
    } catch (e) {
      _logger.e('Exception in getProfileData: $e');
      throw 'Failed to fetch profile data: ${e.toString()}';
    }
  }

  // Upload avatar
  Future<String> uploadAvatar(String filePath) async {
    try {
      if (currentUserId == null) {
        throw 'User not authenticated';
      }

      final fileName =
          '${currentUserId}_${DateTime.now().millisecondsSinceEpoch}.jpg';
      await _supabase.storage.from('avatars').upload(fileName, File(filePath));

      final avatarUrl = _supabase.storage
          .from('avatars')
          .getPublicUrl(fileName);

      // Update the profile with the new avatar URL
      await _supabase
          .from('profiles')
          .update({'avatar': avatarUrl})
          .eq('user_id', currentUserId!);

      return avatarUrl;
    } on StorageException catch (e) {
      _logger.e('StorageException in uploadAvatar: ${e.message}');
      throw 'Storage error: ${e.message}';
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in uploadAvatar: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in uploadAvatar: $e');
      throw 'Failed to upload avatar: ${e.toString()}';
    }
  }

  // Update profile
  Future<ProfileModel> updateProfile(ProfileModel profile) async {
    try {
      if (currentUserId == null) {
        throw 'User not authenticated';
      }

      final profileData = profile.toJson();

      final updatedProfile =
          await _supabase
              .from('profiles')
              .update(profileData)
              .eq('user_id', currentUserId!)
              .select()
              .single();

      return ProfileModel.fromJson(updatedProfile);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in updateProfile: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in updateProfile: $e');
      throw 'Failed to update profile: ${e.toString()}';
    }
  }

  // Create profile if it doesn't exist
  Future<ProfileModel> createProfileIfNotExists({
    required String userId,
    required String nik,
    String? firstName,
    String? lastName,
  }) async {
    try {
      // Check if profile exists
      final existingProfiles = await _supabase
          .from('profiles')
          .select()
          .eq('user_id', userId);

      // If profile exists, return it
      if ((existingProfiles as List).isNotEmpty) {
        return ProfileModel.fromJson(existingProfiles.first);
      }

      // Create new profile
      final profileData = {
        'user_id': userId,
        'nik': nik,
        'first_name': firstName ?? '',
        'last_name': lastName,
      };

      final createdProfile =
          await _supabase
              .from('profiles')
              .insert(profileData)
              .select()
              .single();

      return ProfileModel.fromJson(createdProfile);
    } on PostgrestException catch (error) {
      _logger.e(
        'PostgrestException in createProfileIfNotExists: ${error.message}',
      );
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in createProfileIfNotExists: $e');
      throw 'Failed to create profile: ${e.toString()}';
    }
  }

  // Get profile by user ID
  Future<ProfileModel> getProfileByUserId(String userId) async {
    try {
      final profileData =
          await _supabase
              .from('profiles')
              .select()
              .eq('user_id', userId)
              .single();

      return ProfileModel.fromJson(profileData);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getProfileByUserId: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in getProfileByUserId: $e');
      throw 'Failed to fetch profile data: ${e.toString()}';
    }
  }

  // Get profile by NIK
  Future<ProfileModel?> getProfileByNIK(String nik) async {
    try {
      final profiles =
          await _supabase
              .from('profiles')
              .select()
              .eq('nik', nik)
              .maybeSingle();

      if (profiles == null) return null;
      return ProfileModel.fromJson(profiles);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getProfileByNIK: ${error.message}');
      // If no rows found, return null instead of throwing an exception
      if (error.code == 'PGRST116') return null;
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in getProfileByNIK: $e');
      throw 'Failed to fetch profile by NIK: ${e.toString()}';
    }
  }
}
