import 'dart:async';
import 'package:dio/dio.dart';
import 'package:mypoint_flutter_app/core/network/restful_api_client.dart';
import '../../shared/widgets/base_view/base_response_model.dart';
import '../../shared/widgets/base_view/base_view_model.dart';
import '../../app/config/constants.dart';
import '../../app/routing/app_navigator.dart';
import 'interceptor/network_error_gate.dart';
import 'dio_extra_keys.dart';
import 'dio_http_service.dart';
import 'error_mapper.dart';

typedef ApiCall<T> = Future<BaseResponseModel<T>> Function();
typedef OnSuccess<T> =
    FutureOr<void> Function(T data, BaseResponseModel<T> res);
typedef OnFailure<T> =
    FutureOr<void> Function(
      String message,
      BaseResponseModel<T>? res,
      Object? error,
    );
typedef OnComplete = FutureOr<void> Function();
typedef ApiTask = Future<void> Function();

class RestfulApiViewModel extends BaseViewModel {
  final RestfulAPIClient _apiService = RestfulAPIClient(DioHttpService().dio);
  RestfulAPIClient get client => _apiService;

  Future<void> callApi<T>({
    required ApiCall<T> request,
    required OnSuccess<T> onSuccess,
    OnFailure<T>? onFailure,
    OnComplete? onComplete,
    bool showAppNavigatorDialog = false,
    bool withLoading = true,
    bool trackLoading = true,
    String defaultError = ErrorCodes.commonError,
  }) async {
    if (withLoading) showLoading();
    if (trackLoading) beginTrackedLoading();
    BaseResponseModel<T>? res;
    try {
      res = await request();
      if (res.isSuccess) {
        final T data = res.data as T;
        await onSuccess(data, res);
      } else {
        final msg = res.errorMessage ?? defaultError;
        final hasInternet = await NetworkConnectivity().hasInternet();
        if (showAppNavigatorDialog) {
          AppNavigator.showAlertError(
            content: hasInternet ? msg : ErrorCodes.networkError,
          );
        } else {
          await onFailure?.call(
            hasInternet ? msg : ErrorCodes.networkError,
            res,
            null,
          );
        }
      }
    } catch (e) {
      if (e is DioException &&
          e.requestOptions.extra[kExtraSkipApiErrorHandling] == true) {
        return;
      }
      String msg = defaultError;
      if (e is DioException) {
        final mapped = e.requestOptions.extra['mapped_error'];
        msg =
            (mapped is String && mapped.isNotEmpty)
                ? mapped
                : ErrorMapper.map(e);
      } else {
        msg = ErrorMapper.map(e);
      }
      final hasInternet = await NetworkConnectivity().hasInternet();
      if (showAppNavigatorDialog) {
        AppNavigator.showAlertError(
          content: hasInternet ? msg : ErrorCodes.networkError,
        );
      } else {
        await onFailure?.call(
          hasInternet ? msg : ErrorCodes.networkError,
          res,
          null,
        );
      }
    } finally {
      if (withLoading) hideLoading();
      if (trackLoading) endTrackedLoading();
      onComplete?.call();
    }
  }
}
