import 'package:dio/dio.dart';
import '../errors/errors.dart';
import '../errors/result.dart';

class ApiClient {
  final Dio _dio;

  ApiClient(this._dio);

  Future<Result<T>> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    try {
      final response = await _dio.get<T>(
        path,
        queryParameters: queryParameters,
        options: options,
      );
      return Result.success(response.data as T);
    } on DioException catch (e) {
      return Result.failure(_handleDioError(e));
    } catch (e) {
      return Result.failure(const AppError.unknown(message: 'Unknown error occurred'));
    }
  }

  Future<Result<T>> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    try {
      final response = await _dio.post<T>(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
      );
      return Result.success(response.data as T);
    } on DioException catch (e) {
      return Result.failure(_handleDioError(e));
    } catch (e) {
      return Result.failure(const AppError.unknown(message: 'Unknown error occurred'));
    }
  }

  Future<Result<T>> put<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    try {
      final response = await _dio.put<T>(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
      );
      return Result.success(response.data as T);
    } on DioException catch (e) {
      return Result.failure(_handleDioError(e));
    } catch (e) {
      return Result.failure(const AppError.unknown(message: 'Unknown error occurred'));
    }
  }

  Future<Result<T>> delete<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    try {
      final response = await _dio.delete<T>(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
      );
      return Result.success(response.data as T);
    } on DioException catch (e) {
      return Result.failure(_handleDioError(e));
    } catch (e) {
      return Result.failure(const AppError.unknown(message: 'Unknown error occurred'));
    }
  }

  AppError _handleDioError(DioException error) {
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        return const AppError.network(message: 'Connection timeout');
      case DioExceptionType.connectionError:
        return const AppError.network(message: 'No internet connection');
      case DioExceptionType.badResponse:
        final statusCode = error.response?.statusCode;
        switch (statusCode) {
          case 401:
            return const AppError.unauthorized();
          case 403:
            return const AppError.forbidden();
          case 404:
            return const AppError.notFound();
          case 422:
            return AppError.validation(fieldErrors: _extractValidationErrors(error.response?.data));
          case int s when s >= 500:
            return AppError.server(statusCode: statusCode, message: 'Server error');
          default:
            return AppError.server(statusCode: statusCode, message: 'Request failed');
        }
      case DioExceptionType.cancel:
        return const AppError.unknown(message: 'Request cancelled');
      default:
        return const AppError.unknown();
    }
  }

  Map<String, String>? _extractValidationErrors(dynamic data) {
    if (data is Map<String, dynamic> && data.containsKey('errors')) {
      final errors = data['errors'] as Map<String, dynamic>;
      return errors.map((key, value) {
        if (value is List && value.isNotEmpty) {
          return MapEntry(key, value.first.toString());
        }
        return MapEntry(key, 'Invalid');
      });
    }
    return null;
  }
}