import 'package:dio/dio.dart';
import 'api_exception.dart';
import 'api_interceptor.dart';
import '../storage/token_storage.dart';

class ApiClient {
  final Dio _dio;
  final TokenStorage _tokenStorage;
  final ApiInterceptor _interceptor;

  factory ApiClient({
    required String baseUrl,
    required TokenStorage tokenStorage,
    Dio? dio,
  }) {
    final effectiveDio = dio ?? Dio(BaseOptions(baseUrl: baseUrl));
    final interceptor = ApiInterceptor(
      tokenStorage: tokenStorage,
      dio: effectiveDio,
    );
    effectiveDio.interceptors.add(interceptor);
    return ApiClient._(
      dio: effectiveDio,
      tokenStorage: tokenStorage,
      interceptor: interceptor,
    );
  }

  ApiClient._({
    required Dio dio,
    required TokenStorage tokenStorage,
    required ApiInterceptor interceptor,
  }) : _dio = dio,
       _tokenStorage = tokenStorage,
       _interceptor = interceptor;

  Dio get dio => _dio;

  void setRefreshCallback(Future<bool> Function(String) refresh) {
    _interceptor.onTokenRefresh = () async {
      final token = await _tokenStorage.getRefreshToken();
      if (token == null) return false;
      return refresh(token);
    };
  }

  Future<Response<T>> get<T>(String path, {Options? options}) async {
    try {
      return await _dio.get<T>(path, options: options);
    } on DioException catch (e) {
      throw ApiException.fromDioError(e);
    }
  }

  Future<Response<T>> post<T>(
    String path, {
    dynamic data,
    Options? options,
  }) async {
    try {
      return await _dio.post<T>(path, data: data, options: options);
    } on DioException catch (e) {
      throw ApiException.fromDioError(e);
    }
  }

  Future<Response<T>> patch<T>(
    String path, {
    dynamic data,
    Options? options,
  }) async {
    try {
      return await _dio.patch<T>(path, data: data, options: options);
    } on DioException catch (e) {
      throw ApiException.fromDioError(e);
    }
  }

  Future<Response<T>> delete<T>(
    String path, {
    dynamic data,
    Options? options,
  }) async {
    try {
      return await _dio.delete<T>(path, data: data, options: options);
    } on DioException catch (e) {
      throw ApiException.fromDioError(e);
    }
  }
}
