import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:record/record.dart';
import 'package:path_provider/path_provider.dart';
import '../services/whatsapp_service.dart';
import '../models/conversation_model.dart';
import '../models/message_model.dart';
import 'conversations_controller.dart';

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

  final messages = <MessageModel>[].obs;
  final isLoading = false.obs;
  final isSending = false.obs;

  final inputCtrl = TextEditingController();
  final scrollCtrl = ScrollController();
  final hasText = false.obs;

  // Recording State
  final audioRecord = AudioRecorder();
  final isRecording = false.obs;
  final recordDuration = 0.obs;
  Timer? _recordTimer;

  StreamSubscription? _eventSub;

  ChatController({required this.conversation});

  @override
  void onInit() {
    super.onInit();
    _svc.activeChatId.value = conversation.id;
    
    // Instantly clear the unread count badge in the UI
    try {
      Get.find<ConversationsController>().clearUnreadCount(conversation.id);
    } catch (_) {}

    inputCtrl.addListener(() {
      hasText.value = inputCtrl.text.trim().isNotEmpty;
    });

    loadMessages();
    markAsRead();

    // Listen to push events for new messages and message delivery updates
    _eventSub = _svc.events.listen(_onPushEvent);
  }

  @override
  void onClose() {
    if (_svc.activeChatId.value == conversation.id) {
      _svc.activeChatId.value = null;
    }
    _eventSub?.cancel();
    _recordTimer?.cancel();
    audioRecord.dispose();
    inputCtrl.dispose();
    scrollCtrl.dispose();
    super.onClose();
  }

  // ── Load Messages ────────────────────────────────────────────────────────
  Future<void> loadMessages() async {
    isLoading.value = true;
    try {
      final res = await _svc.getMessages(conversation.id);
      if (res['type'] == 'messages') {
        final List<dynamic> data = res['data'] ?? [];
        final fetched = data.map((m) => MessageModel.fromJson(m as Map<String, dynamic>)).toList();
        
        // Sort chronologically (oldest to newest)
        fetched.sort((a, b) => a.timestamp.compareTo(b.timestamp));
        messages.assignAll(fetched);
        
        // Scroll to bottom after list is rendered
        _scrollToBottom();
      }
    } catch (e) {
      print('[LOAD MESSAGES ERROR] $e');
    } finally {
      isLoading.value = false;
    }
  }

  // ── Send Message ─────────────────────────────────────────────────────────
  Future<void> sendMessage() async {
    final text = inputCtrl.text.trim();
    if (text.isEmpty || isSending.value) return;

    isSending.value = true;
    inputCtrl.clear();

    try {
      final res = await _svc.sendMessage(conversation.id, text);
      if (res['type'] == 'message_sent') {
        final sentMsg = MessageModel.fromJson(res['data'] as Map<String, dynamic>);
        messages.add(sentMsg);
        _scrollToBottom();
      }
    } catch (e) {
      print('[SEND MESSAGE ERROR] $e');
      Get.snackbar('Error', 'Failed to send message: $e',
        backgroundColor: Colors.redAccent.withOpacity(0.8),
        colorText: Colors.white,
      );
    } finally {
      isSending.value = false;
    }
  }

  // ── Send Media Message ───────────────────────────────────────────────────
  Future<void> sendMediaMessage(String base64, String mimetype, String filename, {String? caption}) async {
    isSending.value = true;
    try {
      final res = await _svc.sendMedia(conversation.id, base64, mimetype, filename, caption: caption);
      if (res['type'] == 'message_sent') {
        final sentMsg = MessageModel.fromJson(res['data'] as Map<String, dynamic>);
        messages.add(sentMsg);
        _scrollToBottom();
        
        // Also pre-cache local base64 in mediaCache to display instantly
        _svc.mediaCache[sentMsg.id] = base64;
      } else {
        Get.snackbar('Error', res['message'] ?? 'Failed to send media',
          backgroundColor: Colors.redAccent.withOpacity(0.8),
          colorText: Colors.white,
        );
      }
    } catch (e) {
      print('[SEND MEDIA ERROR] $e');
      Get.snackbar('Error', 'Failed to send media: $e',
        backgroundColor: Colors.redAccent.withOpacity(0.8),
        colorText: Colors.white,
      );
    } finally {
      isSending.value = false;
    }
  }

  // ── Mark Chat as Read ────────────────────────────────────────────────────
  Future<void> markAsRead() async {
    try {
      await _svc.markRead(conversation.id);
    } catch (e) {
      print('[MARK READ ERROR] $e');
    }
  }

  // ── Push Event Handler ───────────────────────────────────────────────────
  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;

        // If the new message is for this chat
        if (chatId == conversation.id) {
          final newMsg = MessageModel.fromJson(msgData);
          
          // Prevent duplicates just in case
          if (!messages.any((m) => m.id == newMsg.id)) {
            messages.add(newMsg);
            _scrollToBottom();
            markAsRead(); // Mark as read since user is actively viewing
          }
        }
        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 (chatId == conversation.id) {
          final index = messages.indexWhere((m) => m.id == messageId);
          if (index != -1) {
            messages[index] = messages[index].copyWith(ack: ack);
          }
        }
        break;
    }
  }

  // ── Helper: Scroll to Bottom ─────────────────────────────────────────────
  void _scrollToBottom() {
    WidgetsBinding.instance.addPostFrameCallback((_) => _performScroll());
    // Also trigger after a short delay to account for async layout calculations
    Future.delayed(const Duration(milliseconds: 150), () {
      _performScroll();
    });
  }

  void _performScroll() {
    if (scrollCtrl.hasClients) {
      scrollCtrl.animateTo(
        scrollCtrl.position.maxScrollExtent,
        duration: const Duration(milliseconds: 250),
        curve: Curves.easeOut,
      );
    }
  }

  // ── Date Separator Logic ─────────────────────────────────────────────────
  List<dynamic> get groupedMessages {
    final list = <dynamic>[];
    if (messages.isEmpty) return list;

    String? lastDate;
    for (final msg in messages) {
      final date = _formatDateSeparator(msg.timestamp);
      if (date != lastDate) {
        list.add(date);
        lastDate = date;
      }
      list.add(msg);
    }
    return list;
  }

  String _formatDateSeparator(int timestamp) {
    final dt = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    final now = DateTime.now();
    final today = DateTime(now.year, now.month, now.day);
    final yesterday = today.subtract(const Duration(days: 1));
    final msgDate = DateTime(dt.year, dt.month, dt.day);

    if (msgDate == today) {
      return 'Today';
    } else if (msgDate == yesterday) {
      return 'Yesterday';
    } else {
      return DateFormat('MMMM d, yyyy').format(dt);
    }
  }

  // ── Audio Recording Engine ───────────────────────────────────────────────
  Future<void> startRecording() async {
    try {
      if (await audioRecord.hasPermission()) {
        final tempDir = await getTemporaryDirectory();
        final path = '${tempDir.path}/rec_${DateTime.now().millisecondsSinceEpoch}.m4a';
        
        await audioRecord.start(
          const RecordConfig(encoder: AudioEncoder.aacLc),
          path: path,
        );
        
        recordDuration.value = 0;
        isRecording.value = true;
        
        _recordTimer?.cancel();
        _recordTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
          recordDuration.value++;
        });
      } else {
        Get.snackbar(
          'Permission Denied',
          'Microphone permission is required to record voice notes.',
          snackPosition: SnackPosition.BOTTOM,
          backgroundColor: Colors.redAccent.withOpacity(0.8),
          colorText: Colors.white,
        );
      }
    } catch (e) {
      print('[START RECORDING ERROR] $e');
    }
  }

  Future<void> stopAndSendRecording() async {
    try {
      _recordTimer?.cancel();
      final path = await audioRecord.stop();
      isRecording.value = false;
      
      if (path != null && recordDuration.value > 0) {
        final file = File(path);
        if (await file.exists()) {
          final bytes = await file.readAsBytes();
          final base64String = base64Encode(bytes);
          
          await sendMediaMessage(
            base64String,
            'audio/mp4', // Recorded as M4A (AAC), perfect for all platforms natively!
            'voice_note.m4a',
          );
        }
      }
    } catch (e) {
      print('[STOP RECORDING ERROR] $e');
    }
  }

  Future<void> cancelRecording() async {
    try {
      _recordTimer?.cancel();
      await audioRecord.stop();
      isRecording.value = false;
      recordDuration.value = 0;
    } catch (e) {
      print('[CANCEL RECORDING ERROR] $e');
    }
  }
}
