import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:tugas_akhir_supabase/screens/community/models/group_message.dart';
import 'package:uuid/uuid.dart';
import 'dart:math';

class MessageSendResult {
  final bool success;
  final String? errorMessage;
  final GroupMessage? message;

  MessageSendResult({required this.success, this.errorMessage, this.message});
}

class GroupMessageService {
  final _supabase = Supabase.instance.client;
  final _uuid = Uuid();
  Timer? _refreshTimer;
  Timer? _readStatusTimer;
  RealtimeChannel? _messagesChannel;
  final Map<String, String> _profilePictureCache = {};

  String? get currentUserId => _supabase.auth.currentUser?.id;

  // Load messages for a specific group
  Future<MessageResult> loadMessages(
    String groupId, {
    bool forceRefresh = false,
    bool loadNew = false,
    List<GroupMessage> existingMessages = const [],
  }) async {
    try {
      print(
        '[FORCE] Loading messages for group: $groupId, forceRefresh: $forceRefresh, loadNew: $loadNew',
      );

      // Use a much shorter timeout
      const timeout = Duration(seconds: 3);

      // More aggressive approach: Try direct query with different options
      List<dynamic> response = [];
      Exception? lastError;
      bool success = false;

      // First attempt - Use RPC function that bypasses RLS
      try {
        response = await _supabase
            .rpc('get_messages_for_group', params: {'group_id_param': groupId})
            .timeout(timeout);

        success = true;
        print(
          '[FORCE] First query attempt successful: ${response.length} messages',
        );
      } catch (e) {
        lastError = e is Exception ? e : Exception(e.toString());
        print('[FORCE] First query attempt failed: $e');

        // Fall back to simple query
        try {
          response = await _supabase
              .from('messages')
              .select()
              .eq('group_id', groupId)
              .order('created_at', ascending: false)
              .limit(20)
              .timeout(timeout);

          success = true;
          print(
            '[FORCE] Simplified query successful: ${response.length} messages',
          );
        } catch (e2) {
          print('[FORCE] Simplified query failed: $e2');
        }
      }

      // If no messages found for default group, create a test message
      if (success &&
          response.isEmpty &&
          groupId == '00000000-0000-0000-0000-000000000001') {
        print(
          '[AUTO] No messages found for default group, creating test message',
        );
        try {
          // Get current user
          final userId = currentUserId;
          if (userId != null) {
            // Try to get username from profiles
            String username = 'User';
            String email = '';

            try {
              final profileData =
                  await _supabase
                      .from('profiles')
                      .select('username, email')
                      .eq('user_id', userId)
                      .single();

              username = profileData['username'] ?? 'User';
              email = profileData['email'] ?? '';
            } catch (e) {
              print('[ERROR] Failed to get profile: $e');
            }

            // Insert test welcome message
            final messageId = _uuid.v4();
            final messageData = {
              'id': messageId,
              'content':
                  'Selamat datang di Grup TaniSM4RT! Ini adalah pesan otomatis. Silakan memulai percakapan.',
              'sender_user_id': userId,
              'sender_username': username,
              'sender_email': email,
              'group_id': groupId,
              'created_at': DateTime.now().toIso8601String(),
            };

            // Insert into database
            final result =
                await _supabase.from('messages').insert(messageData).select();
            print(
              '[AUTO] Created test message: ${result.isNotEmpty ? "Success" : "Failed"}',
            );

            // Add to response
            if (result.isNotEmpty) {
              response = result;
            }

            // Force reload after short delay
            Future.delayed(Duration(milliseconds: 300), () {
              loadMessages(groupId, forceRefresh: true);
            });
          }
        } catch (e) {
          print('[ERROR] Failed to create test message: $e');
        }
      }

      print(
        '[FORCE] Successfully loaded ${response.length} messages from database',
      );
      final messages = _parseMessages(response);
      return MessageResult(messages: messages, hasMore: messages.length == 20);
    } catch (e) {
      print('[ERROR] Failed to load messages after multiple attempts: $e');
      return MessageResult(messages: [], hasMore: false);
    }
  }

  // Load more messages (pagination)
  Future<MessageResult> loadMoreMessages(
    String groupId,
    List<GroupMessage> existingMessages,
  ) async {
    try {
      if (existingMessages.isEmpty) {
        return await loadMessages(groupId);
      }

      const limit = 20;

      // Use the messages_with_sender view
      final response = await _supabase
          .from('messages_with_sender')
          .select()
          .eq('group_id', groupId)
          .order('created_at', ascending: false)
          .limit(limit + existingMessages.length);

      final allMessages = _parseMessages(response);

      // Filter out messages we already have (client-side)
      final existingIds = existingMessages.map((m) => m.id).toSet();
      final newMessages =
          allMessages.where((m) => !existingIds.contains(m.id)).toList();

      return MessageResult(
        messages: newMessages,
        hasMore: newMessages.length == limit,
      );
    } catch (e) {
      print('[ERROR] Failed to load more messages: $e');
      return MessageResult(messages: [], hasMore: false);
    }
  }

  // Parse messages from database response
  List<GroupMessage> _parseMessages(List<dynamic> response) {
    return response
        .map((data) {
          // Check if this is a JSON string from RPC function
          final Map<String, dynamic> messageData;

          if (data is String) {
            // Parse JSON string from RPC function
            try {
              messageData = jsonDecode(data);
            } catch (e) {
              print('[ERROR] Failed to parse message JSON: $e');
              return null;
            }
          } else if (data is Map) {
            // Direct database response
            messageData = Map<String, dynamic>.from(data);
          } else {
            print('[ERROR] Unknown message data format: ${data.runtimeType}');
            return null;
          }

          // Create message from map
          try {
            return GroupMessage.fromMap({
              'id': messageData['id'],
              'content': messageData['content'] ?? '',
              'sender_user_id': messageData['sender_user_id'],
              'sender_username':
                  messageData['profile_username'] ??
                  messageData['sender_username'] ??
                  '',
              'sender_email':
                  messageData['profile_email'] ??
                  messageData['sender_email'] ??
                  '',
              'created_at': messageData['created_at'],
              'image_url': messageData['image_url'],
              'reply_to_id': messageData['reply_to_id'],
              'reply_to_content': messageData['reply_to_content'],
              'reply_to_sender_email': messageData['reply_to_sender_email'],
              'avatar_url': messageData['avatar_url'],
              'group_id': messageData['group_id'],
            });
          } catch (e) {
            print('[ERROR] Failed to create message from data: $e');
            return null;
          }
        })
        .where((message) => message != null)
        .cast<GroupMessage>()
        .toList();
  }

  // Send message to group
  Future<MessageSendResult> sendMessage({
    required String groupId,
    String? text,
    File? imageFile,
    GroupMessage? replyToMessage,
    String? currentUsername,
    String? currentEmail,
    Function(GroupMessage)? onOptimisticUpdate,
  }) async {
    final messageText = text?.trim() ?? '';

    // Allow empty text when sending an image, but require at least one (text or image)
    if (messageText.isEmpty && imageFile == null) {
      return MessageSendResult(
        success: false,
        errorMessage: 'No content to send',
      );
    }

    try {
      // Get current user ID
      final userId = _supabase.auth.currentUser?.id;
      if (userId == null) {
        throw Exception('User not logged in');
      }

      final userEmail = currentEmail ?? _supabase.auth.currentUser?.email ?? '';

      // Generate ID
      final messageId = _uuid.v4(); // UUID valid

      print('[DEBUG] Sending group message: $messageId');
      print(
        '[DEBUG] Group message text: "$messageText", has image: ${imageFile != null}',
      );

      // Prepare message data
      final messageData = {
        'id': messageId,
        'content': messageText, // Always include content even if empty
        'sender_email': userEmail,
        'sender_username': currentUsername ?? userEmail.split('@')[0],
        'sender_user_id': userId,
        'group_id': groupId,
        'created_at': DateTime.now().toIso8601String(),
      };

      // Add reply data if replying to a message
      if (replyToMessage != null) {
        messageData['reply_to_id'] = replyToMessage.id;
        messageData['reply_to_content'] = replyToMessage.content;
        messageData['reply_to_sender_email'] = replyToMessage.senderEmail;
        messageData['reply_to_sender_username'] = replyToMessage.senderUsername;
      }

      // Create an optimistic message object for immediate UI update
      GroupMessage optimisticMessage;

      // For image uploads, create a special optimistic message with local image file
      if (imageFile != null) {
        optimisticMessage = GroupMessage.fromMap({
          ...messageData,
          'created_at': DateTime.now().toIso8601String(),
          'isLocalImage': true,
          'localImageFile': imageFile,
          'content': messageText, // Ensure content is preserved
        });
      } else {
        optimisticMessage = GroupMessage.fromMap({
          ...messageData,
          'created_at': DateTime.now().toIso8601String(),
        });
      }

      // Call the optimistic update function if provided
      if (onOptimisticUpdate != null) {
        onOptimisticUpdate(optimisticMessage);
      }

      // If there's an image, upload it first
      if (imageFile != null) {
        try {
          final imageUrl = await _uploadImage(imageFile);
          if (imageUrl != null) {
            messageData['image_url'] = imageUrl;
          }
        } catch (e) {
          print('[ERROR] Failed to upload image: $e');
          return MessageSendResult(
            success: false,
            errorMessage: 'Gagal mengunggah gambar: ${e.toString()}',
          );
        }
      }

      print(
        '[DEBUG] Saving group message to database: ${messageData.toString()}',
      );
      bool saveSuccess = false;

      try {
        // First try with all data including reply fields
        await _supabase.from('messages').insert(messageData);
        print('[DEBUG] Group message saved successfully');
        saveSuccess = true;
      } catch (e) {
        print('[ERROR] Failed to save group message: $e');

        // If the message has reply data, try without it
        if (replyToMessage != null) {
          print('[DEBUG] Retrying without reply data');

          // Remove reply fields
          final retryData = Map<String, dynamic>.from(messageData);
          retryData.remove('reply_to_id');
          retryData.remove('reply_to_content');
          retryData.remove('reply_to_sender_email');
          retryData.remove('reply_to_sender_username');

          try {
            await _supabase.from('messages').insert(retryData);
            print('[DEBUG] Group message saved without reply data');
            saveSuccess = true;
          } catch (retryError) {
            print('[ERROR] Retry also failed: $retryError');
            return MessageSendResult(
              success: false,
              errorMessage: 'Gagal menyimpan pesan: ${retryError.toString()}',
            );
          }
        } else {
          return MessageSendResult(
            success: false,
            errorMessage: 'Gagal menyimpan pesan: ${e.toString()}',
          );
        }
      }

      // Return success
      return MessageSendResult(
        success: saveSuccess,
        message: optimisticMessage,
      );
    } catch (e) {
      print('[ERROR] Failed to send group message: $e');
      return MessageSendResult(success: false, errorMessage: e.toString());
    }
  }

  // Delete a message
  Future<bool> deleteMessage(GroupMessage message) async {
    try {
      await _supabase.from('messages').delete().eq('id', message.id);
      return true;
    } catch (e) {
      print('[ERROR] Failed to delete message: $e');
      return false;
    }
  }

  // Real-time subscription
  void setupMessagesSubscription(
    String groupId,
    Function(GroupMessage) onNewMessage,
    Function(String, String) onReadStatusUpdate,
  ) async {
    try {
      print('[STREAM] Setting up real-time subscription for group: $groupId');

      // Unsubscribe from any existing subscription
      _messagesChannel?.unsubscribe();
      _messagesChannel = null;

      // Set up new subscription
      _messagesChannel = _supabase
          .channel('public:messages')
          .onPostgresChanges(
            event: PostgresChangeEvent.insert,
            schema: 'public',
            table: 'messages', // Listen specifically to the messages table
            filter: PostgresChangeFilter(
              type: PostgresChangeFilterType.eq,
              column: 'group_id',
              value: groupId,
            ),
            callback: (payload) async {
              try {
                print(
                  '[STREAM] New message received: ${payload.newRecord['id']}',
                );

                // Skip our own messages as we already show them optimistically
                final messageUserId =
                    payload.newRecord['sender_user_id'] as String?;
                if (messageUserId == currentUserId) {
                  print(
                    '[STREAM] Skipping own message in real-time: ${payload.newRecord['id']}',
                  );
                  return;
                }

                // Get complete message data
                final messageId = payload.newRecord['id'] as String;

                // Get the full message data with user info
                final response =
                    await _supabase
                        .from('messages')
                        .select('*, profiles:sender_user_id(*)')
                        .eq('id', messageId)
                        .single();

                // Create full message data with profile info
                final fullMessageData = {
                  'id': response['id'],
                  'content': response['content'] ?? '',
                  'sender_user_id': response['sender_user_id'],
                  'sender_username':
                      response['profiles']?['username'] ??
                      response['sender_username'] ??
                      '',
                  'sender_email':
                      response['profiles']?['email'] ??
                      response['sender_email'] ??
                      '',
                  'created_at': response['created_at'],
                  'image_url': response['image_url'],
                  'reply_to_id': response['reply_to_id'],
                  'reply_to_content': response['reply_to_content'],
                  'reply_to_sender_email': response['reply_to_sender_email'],
                  'avatar_url': response['profiles']?['avatar_url'],
                  'group_id': response['group_id'],
                };

                // Add force refresh timestamp to image URL to prevent caching
                if (fullMessageData['image_url'] != null &&
                    fullMessageData['image_url'].toString().isNotEmpty) {
                  final timestamp = DateTime.now().millisecondsSinceEpoch;
                  fullMessageData['image_url'] =
                      '${fullMessageData['image_url']}?t=$timestamp';
                }

                final message = GroupMessage.fromMap(fullMessageData);

                // Call the callback
                onNewMessage(message);

                print('[STREAM] Real-time message processed: ${message.id}');
              } catch (e) {
                print('[ERROR] Failed to process real-time message: $e');
              }
            },
          )
          .onPostgresChanges(
            event: PostgresChangeEvent.update,
            schema: 'public',
            table: 'read_receipts', // Use read_receipts for message read status
            callback: (payload) {
              final messageId = payload.newRecord['message_id'] as String?;
              final userId = payload.newRecord['user_id'] as String?;

              if (messageId != null && userId != null) {
                onReadStatusUpdate(messageId, userId);
                print(
                  '[STREAM] Read status updated: message=$messageId, user=$userId',
                );
              }
            },
          );
      _messagesChannel!.subscribe(); // Subscribe to the channel

      print('[STREAM] Subscription set up successfully');

      // Check if there are any messages via direct query
      try {
        final count =
            await _supabase
                .from('messages')
                .select('count')
                .eq('group_id', groupId)
                .single();

        final messageCount = count['count'] as int? ?? 0;
        print('[DEBUG] No messages found on initial subscription load');
      } catch (e) {
        print('[ERROR] Error checking message count: $e');
      }
    } catch (e) {
      print('[ERROR] Failed to set up message subscription: $e');
    }
  }

  // Mark messages as read
  Future<void> markMessagesAsRead(List<GroupMessage> messages) async {
    if (messages.isEmpty || currentUserId == null) return;

    try {
      // Filter out messages that are already read by current user
      final unreadMessages = messages.where((msg) => !msg.isRead).toList();

      if (unreadMessages.isEmpty) return;

      // Process each message ID individually using the new function
      for (final message in unreadMessages) {
        try {
          // Pastikan ID pesan adalah UUID yang valid
          final messageId = message.id;
          if (messageId.isEmpty) continue;

          // Use the new function name for handling read receipts
          await _supabase
              .rpc(
                'add_message_read_receipt',
                params: {
                  'p_message_id': messageId.toString(), // Ensure it's string
                  'p_user_id': currentUserId.toString(), // Ensure it's string
                },
              )
              .timeout(
                Duration(seconds: 2),
                onTimeout: () {
                  print(
                    '[WARNING] Timeout marking message as read: $messageId',
                  );
                  return null;
                },
              );
        } catch (e) {
          print('[ERROR] Failed to mark message ${message.id} as read: $e');
          // Lanjutkan ke pesan berikutnya, jangan biarkan satu error menghentikan semua
        }
      }
    } catch (e) {
      print('[ERROR] Failed to mark messages as read: $e');
    }
  }

  // Check if a message has been read by all users in the group
  bool isMessageReadByAll(GroupMessage message) {
    // For now, just check if the current user has read it
    // In a real app, you'd check against all group members
    return message.isRead;
  }

  // Clean up resources
  void dispose() {
    // Clean up subscriptions
    _messagesChannel?.unsubscribe();
    _messagesChannel = null;
    _readStatusTimer?.cancel();
    // _messageStreamController.close(); // This line was removed as per the new_code
  }

  // Upload image
  Future<String?> _uploadImage(File imageFile) async {
    try {
      print('[DEBUG] Starting image upload process');
      // Check if file exists
      if (!await imageFile.exists()) {
        print('[ERROR] Image file does not exist: ${imageFile.path}');
        throw Exception('File does not exist');
      }

      final userId = _supabase.auth.currentUser?.id;
      if (userId == null) {
        print('[ERROR] No authenticated user found');
        throw Exception('User not authenticated');
      }

      final timestamp = DateTime.now().millisecondsSinceEpoch;
      final randomPart = Random().nextInt(10000).toString().padLeft(4, '0');
      final filePath = '$userId-$timestamp-$randomPart.jpg';

      print('[DEBUG] Generated file path: $filePath');

      // Verify file size
      final fileSize = await imageFile.length();
      print(
        '[DEBUG] File size: ${(fileSize / 1024 / 1024).toStringAsFixed(2)} MB',
      );

      if (fileSize > 5 * 1024 * 1024) {
        // 5MB
        print('[ERROR] File too large: ${fileSize / 1024 / 1024} MB');
        throw Exception('Ukuran gambar terlalu besar (maksimal 5MB)');
      }

      // Try hardcoded bucket first to simplify the process
      try {
        print('[DEBUG] Attempting direct upload to images bucket');
        await _supabase.storage
            .from('images')
            .upload(
              filePath,
              imageFile,
              fileOptions: const FileOptions(
                cacheControl: '3600',
                upsert: true,
              ),
            );

        final imageUrl = _supabase.storage
            .from('images')
            .getPublicUrl(filePath);
        print('[DEBUG] Successfully uploaded to images bucket: $imageUrl');
        return imageUrl;
      } catch (e) {
        print('[DEBUG] Direct upload to images bucket failed: $e');
        // Fall back to trying multiple buckets
      }

      // Daftar bucket yang akan dicoba, dalam urutan prioritas
      final bucketOptions = [
        'images',
        'avatars',
        'community',
        'chat-images',
        'public', // Tambahkan bucket public jika ada
      ];
      String? imageUrl;
      Exception? lastError;

      // Coba setiap bucket sampai berhasil
      for (final bucketName in bucketOptions) {
        try {
          print('[DEBUG] Trying upload to bucket: $bucketName');

          await _supabase.storage
              .from(bucketName)
              .upload(
                filePath,
                imageFile,
                fileOptions: const FileOptions(
                  cacheControl: '3600',
                  upsert: true,
                ),
              );

          imageUrl = _supabase.storage.from(bucketName).getPublicUrl(filePath);
          print(
            '[DEBUG] Successfully uploaded to bucket $bucketName: $imageUrl',
          );
          return imageUrl;
        } catch (e) {
          print('[DEBUG] Failed to upload to bucket $bucketName: $e');
          lastError = e as Exception;
          // Continue to next bucket
        }
      }

      // If all buckets failed
      if (lastError != null) {
        throw lastError;
      }
      return null;
    } catch (e) {
      print('[ERROR] Image upload failed: $e');
      rethrow;
    }
  }
}

// Helper class to return messages with pagination info
class MessageResult {
  final List<GroupMessage> messages;
  final bool hasMore;

  MessageResult({required this.messages, required this.hasMore});
}
