import 'dart:async';
import 'package:alt_client/src/contracts/alt_api_result.dart';
import 'package:alt_client/src/integrations/socket/alt_socket_client.dart';
import 'package:alt_client/src/generated/alt/v1/backtest.pb.dart';

/// Repository responsible for handling backtest-related data operations.
///
/// Wraps [AltSocketClient] calls for backtesting runs, details, results, comparisons,
/// and starting backtests, transforming raw socket responses or failures into structured [AltApiResult]s.
class BacktestRepository {
  final AltSocketClient _client;

  BacktestRepository(this._client);

  /// Lists backtest runs matching the given [request].
  Future<AltApiResult<ListBacktestRunsResponse>> listBacktestRuns(
    ListBacktestRunsRequest request, {
    Duration timeout = const Duration(seconds: 2),
  }) async {
    if (!_client.isAlive) {
      return const AltApiResult.transportFailure(
        AltTransportKind.disconnected,
        detail: 'client is not alive',
      );
    }
    try {
      final response = await _client.listBacktestRuns(
        request,
        timeout: timeout,
      );
      final errorInfo = altResponseError(response);
      if (errorInfo != null) {
        return AltApiResult.apiError(AltApiError.fromErrorInfo(errorInfo));
      }
      return AltApiResult.success(response);
    } on TimeoutException catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.timeout,
        detail: e.toString(),
      );
    } on StateError catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.decodeFailure,
        detail: e.toString(),
      );
    } catch (e) {
      final errStr = e.toString().toLowerCase();
      if (errStr.contains('disconnect') ||
          errStr.contains('closed') ||
          errStr.contains('not connected')) {
        return AltApiResult.transportFailure(
          AltTransportKind.disconnected,
          detail: e.toString(),
        );
      }
      return AltApiResult.transportFailure(
        AltTransportKind.unknown,
        detail: e.toString(),
      );
    }
  }

  /// Gets the details of a specific backtest run matching the given [request].
  Future<AltApiResult<GetBacktestRunDetailResponse>> getBacktestRunDetail(
    GetBacktestRunDetailRequest request, {
    Duration timeout = const Duration(seconds: 2),
  }) async {
    if (!_client.isAlive) {
      return const AltApiResult.transportFailure(
        AltTransportKind.disconnected,
        detail: 'client is not alive',
      );
    }
    try {
      final response = await _client.getBacktestRunDetail(
        request,
        timeout: timeout,
      );
      final errorInfo = altResponseError(response);
      if (errorInfo != null) {
        return AltApiResult.apiError(AltApiError.fromErrorInfo(errorInfo));
      }
      return AltApiResult.success(response);
    } on TimeoutException catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.timeout,
        detail: e.toString(),
      );
    } on StateError catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.decodeFailure,
        detail: e.toString(),
      );
    } catch (e) {
      final errStr = e.toString().toLowerCase();
      if (errStr.contains('disconnect') ||
          errStr.contains('closed') ||
          errStr.contains('not connected')) {
        return AltApiResult.transportFailure(
          AltTransportKind.disconnected,
          detail: e.toString(),
        );
      }
      return AltApiResult.transportFailure(
        AltTransportKind.unknown,
        detail: e.toString(),
      );
    }
  }

  /// Gets the results of a specific backtest run matching the given [request].
  Future<AltApiResult<GetBacktestResultResponse>> getBacktestResult(
    GetBacktestResultRequest request, {
    Duration timeout = const Duration(seconds: 2),
  }) async {
    if (!_client.isAlive) {
      return const AltApiResult.transportFailure(
        AltTransportKind.disconnected,
        detail: 'client is not alive',
      );
    }
    try {
      final response = await _client.getBacktestResult(
        request,
        timeout: timeout,
      );
      final errorInfo = altResponseError(response);
      if (errorInfo != null) {
        return AltApiResult.apiError(AltApiError.fromErrorInfo(errorInfo));
      }
      return AltApiResult.success(response);
    } on TimeoutException catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.timeout,
        detail: e.toString(),
      );
    } on StateError catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.decodeFailure,
        detail: e.toString(),
      );
    } catch (e) {
      final errStr = e.toString().toLowerCase();
      if (errStr.contains('disconnect') ||
          errStr.contains('closed') ||
          errStr.contains('not connected')) {
        return AltApiResult.transportFailure(
          AltTransportKind.disconnected,
          detail: e.toString(),
        );
      }
      return AltApiResult.transportFailure(
        AltTransportKind.unknown,
        detail: e.toString(),
      );
    }
  }

  /// Starts a new backtest matching the given [request].
  Future<AltApiResult<StartBacktestResponse>> startBacktest(
    StartBacktestRequest request, {
    Duration timeout = const Duration(seconds: 2),
  }) async {
    if (!_client.isAlive) {
      return const AltApiResult.transportFailure(
        AltTransportKind.disconnected,
        detail: 'client is not alive',
      );
    }
    try {
      final response = await _client.startBacktest(request, timeout: timeout);
      final errorInfo = altResponseError(response);
      if (errorInfo != null) {
        return AltApiResult.apiError(AltApiError.fromErrorInfo(errorInfo));
      }
      return AltApiResult.success(response);
    } on TimeoutException catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.timeout,
        detail: e.toString(),
      );
    } on StateError catch (e) {
      return AltApiResult.transportFailure(
        AltTransportKind.decodeFailure,
        detail: e.toString(),
      );
    } catch (e) {
      final errStr = e.toString().toLowerCase();
      if (errStr.contains('disconnect') ||
          errStr.contains('closed') ||
          errStr.contains('not connected')) {
        return AltApiResult.transportFailure(
          AltTransportKind.disconnected,
          detail: e.toString(),
        );
      }
      return AltApiResult.transportFailure(
        AltTransportKind.unknown,
        detail: e.toString(),
      );
    }
  }
}
