import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

/// WebSocket service for real-time chat communication
class ChatWebSocketService {
  static final ChatWebSocketService _instance =
      ChatWebSocketService._internal();
  factory ChatWebSocketService() => _instance;
  ChatWebSocketService._internal();

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

  bool _isConnected = false;
  bool _isIntentionalClose = false;
  int _reconnectAttempts = 0;
  static const int _maxReconnectAttempts = 5;
  static const Duration _reconnectDelay = Duration(seconds: 3);

  // Callbacks
  Function(Map<String, dynamic>)? onMessageReceived;
  Function()? onConnected;
  Function()? onDisconnected;
  Function(dynamic error)? onError;

  String? _currentChatId;
  String? _wsUrl;

  /// Check if WebSocket is currently connected
  bool get isConnected => _isConnected;

  /// Get current chat ID
  String? get currentChatId => _currentChatId;

  /// Connect to WebSocket for a specific chat
  Future<void> connect({
    required String chatId,
    String baseUrl = "wss://taxglide.amrithaa.net:443/reverb",
    String appKey = "2yj0lyzc9ylw2h03ts6i",
  }) async {
    try {
      // Close existing connection if any
      await disconnect();

      _currentChatId = chatId;
      _wsUrl = "$baseUrl/app/$appKey?protocol=7&client=js&version=1.0";
      _isIntentionalClose = false;
      _reconnectAttempts = 0;

      await _establishConnection();
    } catch (e) {
      debugPrint("❌ Connection error: $e");
      onError?.call(e);
      _scheduleReconnect();
    }
  }

  /// Establish WebSocket connection
  Future<void> _establishConnection() async {
    try {
      debugPrint("🔌 Connecting to: $_wsUrl");

      _channel = WebSocketChannel.connect(Uri.parse(_wsUrl!));

      // Listen to messages
      _subscription = _channel!.stream.listen(
        _handleMessage,
        onError: _handleError,
        onDone: _handleDisconnection,
        cancelOnError: false,
      );

      // Wait a bit for connection to establish
      await Future.delayed(const Duration(milliseconds: 500));

      _isConnected = true;
      _reconnectAttempts = 0;
      debugPrint("✅ WebSocket connected");

      // Subscribe to chat channel
      _subscribeToChannel();

      // Start ping timer
      _startPingTimer();

      onConnected?.call();
    } catch (e) {
      debugPrint("❌ Failed to establish connection: $e");
      _isConnected = false;
      rethrow;
    }
  }

  /// Handle incoming messages
  void _handleMessage(dynamic data) {
    try {
      debugPrint("📨 Received: $data");

      final Map<String, dynamic> message = json.decode(data);

      // Handle Pusher ping
      if (message['event'] == 'pusher:ping') {
        _sendPong();
        return;
      }

      // Handle connection established
      if (message['event'] == 'pusher:connection_established') {
        debugPrint("🔗 Connection established");
        return;
      }

      // Handle subscription succeeded
      if (message['event'] == 'pusher_internal:subscription_succeeded') {
        debugPrint("✅ Subscribed to chat.${_currentChatId}");
        return;
      }

      // Handle custom events (your chat messages)
      if (message['event'] != null &&
          !message['event'].toString().startsWith('pusher')) {
        onMessageReceived?.call(message);
      }
    } catch (e) {
      debugPrint("❌ Error parsing message: $e");
      onError?.call(e);
    }
  }

  /// Handle WebSocket errors
  void _handleError(dynamic error) {
    debugPrint("❌ WebSocket error: $error");
    _isConnected = false;
    onError?.call(error);

    if (!_isIntentionalClose) {
      _scheduleReconnect();
    }
  }

  /// Handle disconnection
  void _handleDisconnection() {
    debugPrint("🔌 WebSocket disconnected");
    _isConnected = false;
    _stopPingTimer();
    onDisconnected?.call();

    if (!_isIntentionalClose) {
      _scheduleReconnect();
    }
  }

  /// Subscribe to chat channel
  void _subscribeToChannel() {
    if (_currentChatId == null) return;

    final subscribeMessage = {
      'event': 'pusher:subscribe',
      'data': {'channel': 'chat.$_currentChatId'},
    };

    _sendMessage(subscribeMessage);
    debugPrint("📡 Subscribing to chat.$_currentChatId");
  }

  /// Send pong response to ping
  void _sendPong() {
    final pongMessage = {'event': 'pusher:pong', 'data': {}};
    _sendMessage(pongMessage);
    debugPrint("🏓 Pong sent");
  }

  /// Start periodic ping timer
  void _startPingTimer() {
    _pingTimer?.cancel();
    _pingTimer = Timer.periodic(const Duration(seconds: 30), (timer) {
      if (_isConnected) {
        // Server will send ping, we just respond with pong
        debugPrint("💓 Heartbeat check");
      }
    });
  }

  /// Stop ping timer
  void _stopPingTimer() {
    _pingTimer?.cancel();
    _pingTimer = null;
  }

  /// Schedule reconnection attempt
  void _scheduleReconnect() {
    if (_isIntentionalClose) return;
    if (_reconnectAttempts >= _maxReconnectAttempts) {
      debugPrint("❌ Max reconnection attempts reached");
      onError?.call(
        "Failed to reconnect after $_maxReconnectAttempts attempts",
      );
      return;
    }

    _reconnectTimer?.cancel();
    _reconnectAttempts++;

    debugPrint(
      "🔄 Scheduling reconnect attempt $_reconnectAttempts in ${_reconnectDelay.inSeconds}s",
    );

    _reconnectTimer = Timer(_reconnectDelay, () async {
      if (!_isIntentionalClose && _wsUrl != null && _currentChatId != null) {
        debugPrint("🔄 Attempting to reconnect...");
        try {
          await _establishConnection();
        } catch (e) {
          debugPrint("❌ Reconnect failed: $e");
          _scheduleReconnect();
        }
      }
    });
  }

  /// Send a message through WebSocket
  void _sendMessage(Map<String, dynamic> message) {
    if (_channel == null || !_isConnected) {
      debugPrint("⚠️ Cannot send message: Not connected");
      return;
    }

    try {
      final jsonMessage = json.encode(message);
      _channel!.sink.add(jsonMessage);
      debugPrint("📤 Sent: $jsonMessage");
    } catch (e) {
      debugPrint("❌ Error sending message: $e");
      onError?.call(e);
    }
  }

  /// Send a custom event to the channel
  void sendEvent({
    required String eventName,
    required Map<String, dynamic> data,
  }) {
    final message = {
      'event': eventName,
      'data': data,
      'channel': 'chat.$_currentChatId',
    };
    _sendMessage(message);
  }

  /// Disconnect from WebSocket
  Future<void> disconnect() async {
    _isIntentionalClose = true;
    _reconnectTimer?.cancel();
    _stopPingTimer();

    await _subscription?.cancel();
    await _channel?.sink.close();

    _channel = null;
    _subscription = null;
    _isConnected = false;
    _currentChatId = null;

    debugPrint("🔌 WebSocket disconnected intentionally");
  }

  /// Switch to a different chat channel
  Future<void> switchChannel(String newChatId) async {
    if (newChatId == _currentChatId) return;

    debugPrint("🔄 Switching from chat.$_currentChatId to chat.$newChatId");

    // Unsubscribe from current channel
    if (_currentChatId != null) {
      final unsubscribeMessage = {
        'event': 'pusher:unsubscribe',
        'data': {'channel': 'chat.$_currentChatId'},
      };
      _sendMessage(unsubscribeMessage);
    }

    // Update chat ID and subscribe to new channel
    _currentChatId = newChatId;
    _subscribeToChannel();
  }

  /// Dispose and cleanup
  void dispose() {
    disconnect();
  }
}
