import 'dart:async';
import 'dart:convert';

import 'package:web_socket_channel/web_socket_channel.dart';

import '../config.dart';

enum WsStatus { connecting, connected, disconnected }

/// کلاینت WebSocket بازی: اتصال با توکن، ارسال/دریافت JSON و اتصال مجدد خودکار.
class WsClient {
  final String token;
  WebSocketChannel? _channel;
  StreamSubscription? _sub;
  Timer? _reconnectTimer;
  bool _disposed = false;
  int _attempt = 0;

  final _messages = StreamController<Map<String, dynamic>>.broadcast();
  final _status = StreamController<WsStatus>.broadcast();

  WsClient(this.token);

  /// پیام‌های دیکدشده‌ی سرور.
  Stream<Map<String, dynamic>> get messages => _messages.stream;

  /// وضعیت اتصال (برای نمایش «در حال اتصال مجدد»).
  Stream<WsStatus> get status => _status.stream;

  void connect() {
    if (_disposed) return;
    _status.add(WsStatus.connecting);
    try {
      final ch = WebSocketChannel.connect(Uri.parse(AppConfig.wsUrl(token)));
      _channel = ch;
      _sub = ch.stream.listen(
        _onData,
        onError: (_) => _scheduleReconnect(),
        onDone: _scheduleReconnect,
        cancelOnError: true,
      );
      _attempt = 0;
      _status.add(WsStatus.connected);
    } catch (_) {
      _scheduleReconnect();
    }
  }

  void _onData(dynamic raw) {
    try {
      final msg = jsonDecode(raw as String);
      if (msg is Map<String, dynamic>) _messages.add(msg);
    } catch (_) {/* پیام نامعتبر نادیده گرفته می‌شود */}
  }

  void send(Map<String, dynamic> msg) {
    _channel?.sink.add(jsonEncode(msg));
  }

  void _scheduleReconnect() {
    if (_disposed) return;
    _status.add(WsStatus.disconnected);
    _sub?.cancel();
    _sub = null;
    _channel = null;
    _reconnectTimer?.cancel();
    // backoff تا حداکثر ۵ ثانیه؛ سرور با همان توکن بازیکن را به میز برمی‌گرداند.
    final delayMs = (500 * (1 << _attempt)).clamp(500, 5000);
    _attempt = (_attempt + 1).clamp(0, 4);
    _reconnectTimer = Timer(Duration(milliseconds: delayMs), connect);
  }

  void dispose() {
    _disposed = true;
    _reconnectTimer?.cancel();
    _sub?.cancel();
    _channel?.sink.close();
    _messages.close();
    _status.close();
  }
}
