import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../services/whatsapp_service.dart';
import '../services/contacts_service.dart';
import '../models/conversation_model.dart';

class ConversationsController extends GetxController {
  final WhatsAppService _svc = Get.find<WhatsAppService>();

  final conversations = <ConversationModel>[].obs;
  final isLoading = false.obs;
  final errorMessage = Rx<String?>(null);

  StreamSubscription? _eventSub;
  StreamSubscription? _readySub;
  Timer? _searchDebounce;

  @override
  void onInit() {
    super.onInit();
    
    // Load local cached conversations first for instant UI response
    _loadCachedConversations();
    
    // Load conversations initially if already ready
    if (_svc.isWaReady.value) {
      loadConversations();
    }

    // React to WhatsApp ready status changes
    _readySub = _svc.isWaReady.listen((ready) {
      if (ready) {
        loadConversations();
      } else {
        conversations.clear();
      }
    });

    // Listen to push events from the server
    _eventSub = _svc.events.listen(_onPushEvent);
  }

  @override
  void onClose() {
    _eventSub?.cancel();
    _readySub?.cancel();
    _searchDebounce?.cancel();
    super.onClose();
  }

  // Helper to resolve contact names from the local address book
  ConversationModel _resolveContactNames(ConversationModel c) {
    if (c.isGroup) return c; // Skip group chats
    
    final parts = c.id.split('@');
    final phoneNumber = parts[0];
    
    final contactsService = Get.find<ContactsService>();
    // Try matching using c.name (which has the formatted number string, e.g. "+962 7 8152 3783")
    String matchedName = contactsService.getContactName(c.name, c.name);
    
    // If it didn't match (i.e. returned c.name), try matching using phoneNumber
    if (matchedName == c.name) {
      matchedName = contactsService.getContactName(phoneNumber, c.name);
    }
    
    return c.copyWith(name: matchedName);
  }

  void clearUnreadCount(String chatId) {
    final index = conversations.indexWhere((c) => c.id == chatId);
    if (index != -1) {
      conversations[index] = conversations[index].copyWith(unreadCount: 0);
      _saveConversationsToCache(conversations.map((c) => c.toJson()).toList());
    }
  }

  // ── Local Caching ────────────────────────────────────────────────────────
  Future<void> _loadCachedConversations() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final cached = prefs.getString('cached_conversations');
      if (cached != null) {
        final List<dynamic> decoded = jsonDecode(cached);
        conversations.assignAll(decoded.map((c) => _resolveContactNames(ConversationModel.fromJson(c as Map<String, dynamic>))));
        print('[CACHE] Loaded ${conversations.length} conversations instantly.');
      }
    } catch (e) {
      print('[CACHE ERROR] $e');
    }
  }

  Future<void> _saveConversationsToCache(List<dynamic> rawData) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('cached_conversations', jsonEncode(rawData));
      print('[CACHE] Saved ${rawData.length} conversations to local cache.');
    } catch (e) {
      print('[CACHE SAVE ERROR] $e');
    }
  }

  // ── Load Conversations ───────────────────────────────────────────────────
  Future<void> loadConversations() async {
    if (!_svc.isWaReady.value || isLoading.value) return;
    
    isLoading.value = true;
    errorMessage.value = null;

    try {
      final res = await _svc.getConversations();
      if (res['type'] == 'conversations') {
        final List<dynamic> data = res['data'] ?? [];
        conversations.assignAll(data.map((c) => _resolveContactNames(ConversationModel.fromJson(c as Map<String, dynamic>))));
        _saveConversationsToCache(data);
      } else {
        errorMessage.value = res['message'] ?? 'Failed to load conversations';
      }
    } catch (e) {
      errorMessage.value = e.toString();
    } finally {
      isLoading.value = false;
    }
  }

  // ── Search Conversations ──────────────────────────────────────────────────
  void search(String query) {
    _searchDebounce?.cancel();
    if (query.trim().isEmpty) {
      loadConversations();
      return;
    }

    _searchDebounce = Timer(const Duration(milliseconds: 400), () async {
      isLoading.value = true;
      try {
        final res = await _svc.searchConversations(query);
        if (res['type'] == 'conversations') {
          final List<dynamic> data = res['data'] ?? [];
          conversations.assignAll(data.map((c) => ConversationModel.fromJson(c as Map<String, dynamic>)));
        }
      } catch (e) {
        print('[SEARCH ERROR] $e');
      } finally {
        isLoading.value = false;
      }
    });
  }

  // ── Handle Incoming Socket Push Events ──────────────────────────────────
  void _onPushEvent(Map<String, dynamic> event) {
    final type = event['type'] as String?;
    if (type == null) return;

    switch (type) {
      case 'new_message':
        final chatId = event['chatId'] as String?;
        final msgData = event['data'] as Map<String, dynamic>?;
        if (chatId == null || msgData == null) return;

        // Create the LastMessage object
        final lastMsg = LastMessageModel(
          body: msgData['body'] ?? '',
          timestamp: msgData['timestamp'] ?? 0,
          fromMe: msgData['fromMe'] ?? false,
          hasMedia: msgData['hasMedia'] ?? false,
        );

        // Find existing conversation and update it
        final index = conversations.indexWhere((c) => c.id == chatId);
        if (index != -1) {
          final existing = conversations[index];
          final isCurrentActiveChat = _svc.activeChatId.value == chatId;
          final updated = existing.copyWith(
            lastMessage: lastMsg,
            timestamp: lastMsg.timestamp,
            unreadCount: (lastMsg.fromMe || isCurrentActiveChat) ? 0 : existing.unreadCount + 1,
          );
          conversations.removeAt(index);
          conversations.insert(0, updated);
        } else {
          // If conversation is not loaded, trigger a silent full reload to fetch it
          loadConversationsSilently();
        }
        break;

      case 'message_ack':
        final messageId = event['messageId'] as String?;
        final chatId = event['chatId'] as String?;
        final ack = event['ack'] as int?;
        if (chatId == null || messageId == null || ack == null) return;

        // If the last message in a conversation was acknowledged, update it
        final index = conversations.indexWhere((c) => c.id == chatId);
        if (index != -1) {
          // We can refresh silently if it is the current conversation's last message.
          // Since ack is simple, a quick silent refresh guarantees correct ack state.
          loadConversationsSilently();
        }
        break;

      case 'ready':
      case 'authenticated':
        loadConversationsSilently();
        break;

      case 'disconnected':
        conversations.clear();
        break;
    }
  }

  Future<void> loadConversationsSilently() async {
    if (!_svc.isWaReady.value) return;
    try {
      final res = await _svc.getConversations();
      if (res['type'] == 'conversations') {
        final List<dynamic> data = res['data'] ?? [];
        conversations.assignAll(data.map((c) => _resolveContactNames(ConversationModel.fromJson(c as Map<String, dynamic>))));
        _saveConversationsToCache(data);
      }
    } catch (_) {}
  }
}
