import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:get_it/get_it.dart';
import 'package:tugas_akhir_supabase/services/user_presence_service.dart';
import 'package:tugas_akhir_supabase/services/session_manager.dart';

class AuthServices {
  final SupabaseClient _supabase = Supabase.instance.client;

  // Sign in with email and password
  Future<AuthResponse> signInWithEmailPassword(
    String email,
    String password,
  ) async {
    try {
      // Tambahkan timeout untuk mencegah permintaan menggantung
      final response = await _supabase.auth
          .signInWithPassword(email: email, password: password)
          .timeout(
            const Duration(seconds: 15),
            onTimeout: () {
              throw TimeoutException(
                'Koneksi timeout. Silakan coba lagi nanti.',
              );
            },
          );

      // Register and initialize UserPresenceService after successful login
      await _registerUserPresenceService();

      // Update session manager
      SessionManager.setUserLoggedIn(true);

      return response;
    } catch (e) {
      debugPrint('Error saat login: $e');

      // Re-throw exception untuk ditangani di UI
      rethrow;
    }
  }

  // Register and initialize UserPresenceService
  Future<void> _registerUserPresenceService() async {
    try {
      final currentUser = _supabase.auth.currentUser;
      if (currentUser == null) return;

      // Register the service if not already registered
      if (!GetIt.instance.isRegistered<UserPresenceService>()) {
        debugPrint('Registering UserPresenceService after login');
        GetIt.instance.registerSingleton<UserPresenceService>(
          UserPresenceService(),
        );
        await GetIt.instance<UserPresenceService>().initialize();
      } else {
        // If already registered, just initialize it
        debugPrint('UserPresenceService already registered, initializing');
        await GetIt.instance<UserPresenceService>().initialize();
      }
    } catch (e) {
      debugPrint('Error registering UserPresenceService: $e');
    }
  }

  // Sign Up with email and password
  Future<AuthResponse> signUpWithEmailPassword(
    String email,
    String password,
  ) async {
    final response = await _supabase.auth.signUp(
      email: email,
      password: password,
    );

    return response;
  }

  // Sign Out
  Future<void> signOut() async {
    try {
      // Dispose UserPresenceService if registered
      if (GetIt.instance.isRegistered<UserPresenceService>()) {
        debugPrint('Disposing UserPresenceService during sign out');
        GetIt.instance<UserPresenceService>().dispose();

        // Unregister the service
        if (GetIt.instance.isRegistered<UserPresenceService>()) {
          GetIt.instance.unregister<UserPresenceService>();
        }
      }

      // Update session manager
      SessionManager.setUserLoggedIn(false);

      // Sign out from Supabase
      await _supabase.auth.signOut();
    } catch (e) {
      debugPrint('Error during sign out: $e');
      // Still try to sign out from Supabase even if there was an error
      await _supabase.auth.signOut();
    }
  }

  // Get current user ID
  String? getCurrentUserId() {
    final session = _supabase.auth.currentSession;
    final user = session?.user;
    return user?.id;
  }

  // Request password reset (sends email with OTP)
  Future<void> forgotPassword(String email, {String? redirectUrl}) async {
    try {
      await _supabase.auth.resetPasswordForEmail(
        email,
        redirectTo: redirectUrl,
      );
    } catch (e) {
      debugPrint('Error sending password reset email: $e');
      throw Exception('Gagal mengirim email reset password: $e');
    }
  }

  // Verify OTP for password reset or signup
  Future<AuthResponse> verifyOTP({
    required String email,
    required String token,
    required OtpType type,
  }) async {
    try {
      final response = await _supabase.auth.verifyOTP(
        email: email,
        token: token,
        type: type,
      );

      return response;
    } catch (e) {
      debugPrint('Error verifying OTP: $e');
      throw Exception('Verifikasi OTP gagal: $e');
    }
  }

  // Reset password (after OTP verification)
  Future<void> resetPassword(String newPassword) async {
    try {
      await _supabase.auth.updateUser(UserAttributes(password: newPassword));
    } catch (e) {
      debugPrint('Error resetting password: $e');
      throw Exception('Gagal mengubah password: $e');
    }
  }

  // Check if user is logged in (without checking timeout)
  bool isUserLoggedIn() {
    return _supabase.auth.currentSession != null;
  }

  // Get current authenticated user
  User? getCurrentUser() {
    return _supabase.auth.currentUser;
  }

  // Get current user email
  String? getCurrentUserEmail() {
    final session = _supabase.auth.currentSession;
    final user = session?.user;
    return user?.email;
  }

  // Get user role
  Future<String?> getUserRole() async {
    final userId = getCurrentUserId();
    if (userId == null) return null;

    try {
      debugPrint('Mencoba mendapatkan role untuk user ID: $userId');

      // Coba query langsung ke tabel
      final response =
          await _supabase
              .from('user_roles')
              .select('role')
              .eq('user_id', userId)
              .maybeSingle();

      debugPrint('Response dari query user_roles: $response');

      if (response != null) {
        final role = response['role'] as String?;
        debugPrint('Role ditemukan: $role');
        return role;
      } else {
        debugPrint('Tidak ada role yang ditemukan untuk user ID: $userId');
        return null;
      }
    } catch (e) {
      debugPrint('Error getting user role: $e');
      return null;
    }
  }

  // Check if user is admin
  Future<bool> isAdmin() async {
    try {
      final userId = getCurrentUserId();
      debugPrint('Checking admin status for user ID: $userId');

      if (userId == null) {
        debugPrint('User ID is null, not an admin');
        return false;
      }

      // First try: cek langsung dari tabel tanpa RLS
      try {
        debugPrint('Trying direct table query for admin check...');
        final response =
            await _supabase
                .from('user_roles')
                .select('role')
                .eq('user_id', userId)
                .eq('role', 'admin')
                .maybeSingle();

        final isAdmin = response != null;
        debugPrint('Direct table query result: $isAdmin');
        if (isAdmin) return true;
      } catch (directError) {
        debugPrint('Direct table query failed: $directError');
      }

      // Second try: gunakan fungsi is_admin_no_rls untuk menghindari infinite recursion
      try {
        debugPrint('Trying RPC function for admin check...');
        final response = await _supabase.rpc(
          'is_admin_no_rls',
          params: {'input_user_id': userId},
        );

        debugPrint('Admin check RPC response: $response');

        // Response dari RPC akan berupa boolean
        final isAdmin = response == true;
        debugPrint('RPC admin check result: $isAdmin');
        return isAdmin;
      } catch (rpcError) {
        debugPrint('RPC function failed: $rpcError');
      }

      // Third try: cek dengan query yang lebih sederhana
      try {
        debugPrint('Trying simple query for admin check...');
        final response = await _supabase
            .from('user_roles')
            .select('*')
            .eq('user_id', userId)
            .eq('role', 'admin')
            .limit(1);

        final isAdmin = response.isNotEmpty;
        debugPrint('Simple query result: $isAdmin');
        return isAdmin;
      } catch (simpleError) {
        debugPrint('Simple query failed: $simpleError');
      }

      // If all methods fail, return false
      debugPrint('All admin check methods failed, returning false');
      return false;
    } catch (e) {
      debugPrint('Error checking admin status: $e');
      return false;
    }
  }

  // Force refresh user session
  Future<void> refreshSession() async {
    try {
      final session = _supabase.auth.currentSession;
      if (session != null) {
        await _supabase.auth.refreshSession();
        debugPrint('Session refreshed successfully');
      }
    } catch (e) {
      debugPrint('Error refreshing session: $e');
    }
  }

  // Get all admins
  Future<List<Map<String, dynamic>>> getAllAdmins() async {
    try {
      final response = await _supabase
          .from('user_roles')
          .select('user_id')
          .eq('role', 'admin');

      return List<Map<String, dynamic>>.from(response);
    } catch (e) {
      debugPrint('Error getting all admins: $e');
      return [];
    }
  }

  // Count admins
  Future<int> countAdmins() async {
    try {
      final admins = await getAllAdmins();
      return admins.length;
    } catch (e) {
      debugPrint('Error counting admins: $e');
      return 0;
    }
  }

  Future<AuthResponse> signInWithEmailAndPassword(
    String email,
    String password,
  ) async {
    try {
      debugPrint('Attempting to sign in with email: $email');
      final response = await _supabase.auth.signInWithPassword(
        email: email,
        password: password,
      );
      debugPrint('Sign in response: ${response.session != null}');
      return response;
    } on AuthException catch (e) {
      debugPrint('AuthException during sign in: ${e.message}');
      rethrow;
    } on PostgrestException catch (e) {
      debugPrint('PostgrestException during sign in: ${e.message}');

      // Check for infinite recursion error
      if (e.code == '42P17' && e.message.contains('infinite recursion')) {
        throw Exception(
          'Terjadi masalah pada kebijakan database. Silakan hubungi admin untuk memperbaiki kebijakan users atau gunakan tombol "Perbaiki Database" jika Anda adalah admin.',
        );
      }

      rethrow;
    } catch (e) {
      debugPrint('Unknown exception during sign in: $e');

      // Check for infinite recursion error in generic exception
      if (e.toString().contains('infinite recursion') &&
          e.toString().contains('42P17')) {
        throw Exception(
          'Terjadi masalah pada kebijakan database. Silakan hubungi admin untuk memperbaiki kebijakan users atau gunakan tombol "Perbaiki Database" jika Anda adalah admin.',
        );
      }

      rethrow;
    }
  }
}
