// ignore_for_file: empty_catches

import 'dart:async';

import 'dart:io';

import 'package:clashmi/app/utils/log.dart';
import 'package:web_socket_channel/io.dart';

class Websocket {
  final String url;
  final Duration? connectionTimeout;
  final String? userAgent;
  final String? proxy;
  final Function(String message) onMessage;
  final Function()? onDone;
  final Function(dynamic error)? onError;

  bool _connecting = false;
  HttpClient? _httpClient;
  StreamSubscription<dynamic>? _subscriptions;

  Websocket({
    required this.url,
    this.connectionTimeout,
    this.userAgent,
    this.proxy,
    required this.onMessage,
    this.onDone,
    this.onError,
  });

  Future<void> connect() async {
    if (_httpClient != null) {
      return;
    }
    if (_connecting) {
      return;
    }
    _connecting = true;
    try {
      await _subscriptions?.cancel();
      _httpClient?.close(force: true);
      _httpClient = null;
      _httpClient = HttpClient();
      _httpClient!.userAgent = userAgent;
      _httpClient!.connectionTimeout =
          connectionTimeout ?? const Duration(seconds: 3);
      _httpClient!.findProxy = (Uri uri) => proxy ?? "DIRECT";

      {
        WebSocket webSocket = await WebSocket.connect(
          url,
          customClient: _httpClient,
        );

        _subscriptions = IOWebSocketChannel(webSocket).stream.listen(
          (message) {
            onMessage.call(message);
          },
          onDone: () {
            disconnect();
            onDone?.call();
          },
          onError: (error) {
            onError?.call(error);
          },
        );
      }
    } catch (err) {
      Log.w("Websocket.connect exception ${err.toString()}");
      disconnect();
    }
    _connecting = false;
  }

  Future<void> disconnect() async {
    await _subscriptions?.cancel();
    _subscriptions = null;
    _httpClient?.close();
    _httpClient = null;
    _connecting = false;
  }

  bool connected() {
    return _subscriptions != null;
  }
}
