import 'dart:async';
import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:taxglide/services/notification_service.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:taxglide/controller/api_contoller.dart';

class NotificationWebSocket {
  static final NotificationWebSocket _instance =
      NotificationWebSocket._internal();
  factory NotificationWebSocket() => _instance;
  NotificationWebSocket._internal();

  WebSocketChannel? _channel;
  StreamSubscription? _subscription;
  Timer? _reconnectTimer;

  bool _isConnected = false;
  bool _isIntentionalClose = false;

  String? _userId;
  String? _wsUrl;
  ProviderContainer? _container;

  int _reconnectAttempts = 0;
  static const int _maxReconnectAttempts = 10;

  /// ChatId the user is currently VIEWING.
  String? _activeChatId;

  /// ChatId whose `chat.<id>` channel we are subscribed to on this WS.
  String? _subscribedChatChannelId;

  // ─────────────────────────── Public API ────────────────────────────────────

  /// Call from LiveChatScreen.initState().
  void setActiveChatId(String chatId) {
    _activeChatId = chatId;
    debugPrint('NotificationWS: viewing chat $chatId');
    _subscribeToChatChannel(chatId);
  }

  /// Call from LiveChatScreen.dispose()
  void clearActiveChatId() {
    debugPrint('NotificationWS: left chat $_activeChatId');
    _activeChatId = null;
  }

  /// Subscribe to a chat channel for updates.
  void watchChatChannel(String chatId) {
    debugPrint('Eye NotificationWS: watchChatChannel($chatId)');
    _subscribeToChatChannel(chatId);
  }

  /// Manually trigger a refresh for all providers watching `notificationTriggerProvider`.
  void triggerRefresh() {
    try {
      _container?.read(notificationTriggerProvider.notifier).update((state) => state + 1);
      debugPrint('🔔 NotificationWS: manual refresh triggered');
    } catch (e) {
      debugPrint('⚠️ NotificationWS: triggerRefresh failed: $e');
    }
  }

  /// Connect (or skip if already connected for the same user).
  Future<void> connect({
    required String userId,
    required ProviderContainer container,
    String baseUrl = 'wss://app.taxglide.in:443/reverb',
    String appKey = '2yj0lyzc9ylw2h03ts6i',
  }) async {
    if (_isConnected && _userId == userId) {
      debugPrint('⚡ NotificationWS: already connected for user $userId');
      return;
    }

    await disconnect();

    _userId = userId;
    _container = container;
    _wsUrl = '$baseUrl/app/$appKey?protocol=7&client=js&version=1.0';
    _isIntentionalClose = false;
    _reconnectAttempts = 0;

    _openSocket();
  }

  // ──────────────────────── Socket lifecycle ──────────────────────────────────

  void _openSocket() {
    try {
      debugPrint('🔌 NotificationWS connecting…');
      _channel = WebSocketChannel.connect(Uri.parse(_wsUrl!));

      _subscription = _channel!.stream.listen(
        _handleMessage,
        onError: _handleError,
        onDone: _handleDone,
      );

      _isConnected = true;

      _subscribeToUserChannel();

      // Re-subscribe to chat channel after reconnect if we had one before.
      final chatId = _subscribedChatChannelId;
      if (chatId != null) {
        _sendRaw({
          'event': 'pusher:subscribe',
          'data': {'channel': 'chat.$chatId'},
        });
        debugPrint('📡 NotificationWS: re-subscribed to chat.$chatId');
      }
    } catch (e) {
      debugPrint('❌ NotificationWS connect error: $e');
      _isConnected = false;
      _scheduleReconnect();
    }
  }

  void _subscribeToUserChannel() {
    if (_userId == null) return;
    _sendRaw({
      'event': 'pusher:subscribe',
      'data': {'channel': 'user-chat-notification.$_userId'},
    });
    debugPrint(
      '📡 NotificationWS: subscribed to user-chat-notification.$_userId',
    );
  }

  void _subscribeToChatChannel(String chatId) {
    if (_subscribedChatChannelId == chatId) return; // already subscribed

    // Unsubscribe previous chat channel
    if (_subscribedChatChannelId != null) {
      _sendRaw({
        'event': 'pusher:unsubscribe',
        'data': {'channel': 'chat.$_subscribedChatChannelId'},
      });
      debugPrint(
        '📡 NotificationWS: unsubscribed from chat.$_subscribedChatChannelId',
      );
    }

    _subscribedChatChannelId = chatId;
    _sendRaw({
      'event': 'pusher:subscribe',
      'data': {'channel': 'chat.$chatId'},
    });
    debugPrint('📡 NotificationWS: subscribed to chat.$chatId');
  }

  // ──────────────────────── Message handler ──────────────────────────────────

  void _handleMessage(dynamic raw) {
    debugPrint('🔔 NotificationWS raw: $raw');

    try {
      final msg = jsonDecode(raw) as Map<String, dynamic>;
      final event = msg['event']?.toString() ?? '';

      // ── Pusher connection established → re-subscribe ──────────────────────
      if (event == 'pusher:connection_established') {
        debugPrint('🔗 NotificationWS: connection established');
        _subscribeToUserChannel();
        final chatId = _subscribedChatChannelId;
        if (chatId != null) {
          _sendRaw({
            'event': 'pusher:subscribe',
            'data': {'channel': 'chat.$chatId'},
          });
        }
        return;
      }

      // ── Skip all other Pusher internal events ─────────────────────────────
      if (event.startsWith('pusher')) return;
      if (event.startsWith('pusher_internal')) return;

      // ── Refresh Riverpod badge count ──────────────────────────────────────
      try {
        _container?.read(notificationTriggerProvider.notifier).state++;
      } catch (_) {}

      // ── Determine which chat this message belongs to ──────────────────────
      String? incomingChatId;
      final channelStr = msg['channel']?.toString() ?? '';
      if (channelStr.startsWith('chat.')) {
        incomingChatId = channelStr.split('.').last;
      }

      // ── Parse the payload ─────────────────────────────────────────────────
      final rawData = msg['data'];
      if (rawData == null) return;

      dynamic data;
      try {
        data = rawData is String ? jsonDecode(rawData) : rawData;
      } catch (_) {
        data = rawData;
      }

      // Flatten list → use first element
      if (data is List && data.isNotEmpty) {
        data = data[0];
      }

      if (data is! Map) {
        debugPrint('⚠️ NotificationWS: unrecognised data format, skipping');
        return;
      }

      final dataMap = Map<String, dynamic>.from(data);
      debugPrint('🔔 NotificationWS data keys: ${dataMap.keys.toList()}');

      // ── Skip ping-only payloads {status, reload} ──────────────────────────
      // The backend first fires a lightweight notification-trigger event that
      // only contains `status` and `reload`.  There is nothing to display yet;
      // the full message list arrives in the next event on the same channel.
      final onlyStatusReload =
          dataMap.containsKey('status') &&
          dataMap.containsKey('reload') &&
          !dataMap.containsKey('message') &&
          !dataMap.containsKey('chat_by');
      if (onlyStatusReload) {
        debugPrint('🔔 NotificationWS: reload ping received, badge refreshed');
        return;
      }

      // ── Skip user's own messages ──────────────────────────────────────────
      final chatBy = dataMap['chat_by']?.toString().toLowerCase() ?? '';
      if (chatBy == 'user' || chatBy == 'customer') {
        debugPrint('🔕 NotificationWS: own message, skipping');
        return;
      }

      // ── Extract routing info if needed for internal state ─────────────
      final String? page =
          dataMap['page']?.toString() ?? dataMap['type']?.toString();
      final String? pageId =
          dataMap['page_id']?.toString() ?? dataMap['pageId']?.toString();

      if (page == null || page.isEmpty || pageId == null || pageId.isEmpty) {
        // Chat Notification fallback
        incomingChatId ??=
            dataMap['chat_id']?.toString() ??
            dataMap['chatId']?.toString() ??
            (dataMap['parent_tag'] != null && dataMap['parent_tag'] is Map
                ? dataMap['parent_tag']['id']?.toString()
                : null) ??
            dataMap['id']?.toString();

        if (incomingChatId != null && incomingChatId != '0') {
          _subscribeToChatChannel(incomingChatId);
        }
      }

      // ── Show Foreground Notification ─────────────────────────────────────
      // Only show if it's NOT a message for the currently active chat screen.
      // Pass incomingChatId as the dedup tag so that if FCM already fired a
      // notification for this same chat, this one *replaces* it on Android
      // instead of showing a second notification.
      if (incomingChatId != _activeChatId) {
        // Prefer the sender name from dataMap for the title.
        final String senderTitle =
            dataMap['sender_name']?.toString() ??
            dataMap['name']?.toString() ??
            dataMap['chat_by']?.toString() ??
            'New Message';

        final RemoteMessage fcmLikeMessage = RemoteMessage(
          data: dataMap,
          notification: RemoteNotification(
            title: senderTitle,
            body: dataMap['message']?.toString() ?? 'You have a new message',
          ),
        );

        Get.find<NotificationService>().showForegroundNotification(
          fcmLikeMessage,
          tag: incomingChatId, // dedup key – same chat collapses into 1 notif
        );
      }
    } catch (e, st) {
      debugPrint('❌ NotificationWS _handleMessage error: $e\n$st');
    }
  }

  void _sendRaw(Map<String, dynamic> message) {
    try {
      _channel?.sink.add(jsonEncode(message));
    } catch (e) {
      debugPrint('❌ NotificationWS send error: $e');
    }
  }

  // ──────────────────────── Reconnect ────────────────────────────────────────

  void _handleError(dynamic error) {
    debugPrint('❌ NotificationWS error: $error');
    _isConnected = false;
    _scheduleReconnect();
  }

  void _handleDone() {
    debugPrint('🔌 NotificationWS: connection closed');
    _isConnected = false;
    if (!_isIntentionalClose) _scheduleReconnect();
  }

  void _scheduleReconnect() {
    if (_isIntentionalClose || _reconnectAttempts >= _maxReconnectAttempts) {
      return;
    }
    _reconnectAttempts++;
    final delay = Duration(seconds: _reconnectAttempts * 2);
    debugPrint(
      '🔄 NotificationWS: reconnect #$_reconnectAttempts in ${delay.inSeconds}s',
    );
    _reconnectTimer = Timer(delay, () {
      if (!_isIntentionalClose) _openSocket();
    });
  }

  Future<void> disconnect() async {
    _isIntentionalClose = true;
    _reconnectTimer?.cancel();
    await _subscription?.cancel();
    await _channel?.sink.close();
    _isConnected = false;
    _subscribedChatChannelId = null;
    debugPrint('🔌 NotificationWS: disconnected');
  }
}
