import 'dart:developer';
import 'dart:io';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:http/io_client.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:signalr_core/signalr_core.dart';

abstract class SignalrRepo {
  Future<bool?> startHubConnection({
    required String deviceIp,
    required Function(List<Object?>?) onHubTicketCall,
    required Function(dynamic) onHubConfigCall,
    required Function(dynamic) onHubConnecting,
    required Function(dynamic) onHubReconnected,
    required Function(dynamic) onHubDisconnected,
  });

  Future<void> closeConnection();
}

class SignalrRepoImp implements SignalrRepo {
  HubConnection? connection;
  LoggerService loggerService;

  bool get isConnected => connection?.state == HubConnectionState.connected;

  SignalrRepoImp({this.connection, required this.loggerService});

  @override
  Future<bool?> startHubConnection({
    required String deviceIp,
    required Function(List<Object?>?) onHubTicketCall,
    required Function(dynamic) onHubConfigCall,
    required Function(dynamic) onHubConnecting,
    required Function(dynamic) onHubReconnected,
    required Function(dynamic) onHubDisconnected,
  }) async {
    try {
      String hubBaseURL = ApiConstants.baseUrlHub;
      if ((connection != null) && (connection!.state == HubConnectionState.connected || connection!.state == HubConnectionState.connecting)) {
        return true;
      }
      connection = HubConnectionBuilder()
          .withUrl(
              "$hubBaseURL?IPAddress=$deviceIp",
              HttpConnectionOptions(
                client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true),
                logging: (level, message) => log(message),
              ))
          .withAutomaticReconnect([0, 2000, 5000, 10000, 30000]) // Custom retry delays
          .build();

      connection!.serverTimeoutInMilliseconds = 120000; // 2 minutes
      connection!.keepAliveIntervalInMilliseconds = 15000; // 15 seconds keep-alive
      int reconnectAttempts = 0;
      const int maxReconnectAttempts = 10;
      const Duration reconnectDelay = Duration(seconds: 5);
      connection!.onclose((exception) async {
        log(exception.toString());
        onHubDisconnected(exception);
        if (reconnectAttempts < maxReconnectAttempts) {
          reconnectAttempts++;
          loggerService.logToFile(message: "SignalR reconnect attempt #$reconnectAttempts", source: "startHubConnection -> signalR_repo.dart", type: LogTypeEnum.data);
          await Future.delayed(reconnectDelay);
          try {
            await connection!.start();
            loggerService.logToFile(message: "SignalR reconnected after disconnect", source: "startHubConnection -> signalR_repo.dart", type: LogTypeEnum.data);
            reconnectAttempts = 0; // Reset on success
          } catch (e) {
            loggerService.logError("Reconnect failed: $e");
          }
        } else {
          loggerService.logError("Max SignalR reconnect attempts reached.");
        }
      });

      connection!.onreconnecting((exception) => onHubConnecting(exception));
      connection!.onreconnected((connectionId) {
        log(connectionId.toString());
        onHubReconnected(connectionId);
      });

      connection!.on(ApiConstants.sendQLinePatientCall, (List<Object?>? message) {
        onHubTicketCall(message);
      });

      connection!.on(ApiConstants.sendQLineConfig, (List<Object?>? message) {
        onHubConfigCall(message);
      });

      await connection!.start();
      return true;
    } catch (e) {
      loggerService.logError(e.toString());
      loggerService.logToFile(message: e.toString(), source: "startHubConnection -> signalR_repo.dart", type: LogTypeEnum.error);
      return false;
    }
  }

  bool getHubConnectionState() {
    if (connection == null) return false;
    log("connectionState: ${connection!.state}");
    if (connection!.state == HubConnectionState.connected || connection!.state == HubConnectionState.connecting) return true;
    if (connection!.state == HubConnectionState.disconnected || connection!.state == HubConnectionState.disconnecting) return false;
    return false;
  }

  @override
  closeConnection() async {
    if (connection!.state == HubConnectionState.connected || connection!.state == HubConnectionState.connecting) {
      await connection!.stop();
    }
  }
}
