import 'dart:convert';
import 'package:get/get.dart' as Get;
import 'package:dio/dio.dart';
import 'package:sk_base_mobile/models/base_response.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/config.dart';
import 'package:sk_base_mobile/services/storage.service.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';

import '../constants/constants.dart';

class DioService extends Get.GetxService {
  static DioService get to => Get.Get.find();
  static Dio get dio => _dio;
  static late Dio _dio;
  List<String> whiteList = [Urls.login];
  BaseOptions dioBaseOptions = BaseOptions(
      connectTimeout: const Duration(seconds: 20),
      baseUrl: '${GloablConfig.BASE_URL}',
      followRedirects: true);

  late CancelToken cancelToken;

  /// Initialize the dio instance
  Future<DioService> init() async {
    _dio = Dio(dioBaseOptions);
    _dio.interceptors.add(InterceptorsWrapper(
        onRequest: onRequest, onResponse: onResponse, onError: onError));
    cancelToken = CancelToken();
    return this;
  }

  void onError(DioException e, ErrorInterceptorHandler handler) async {
    if (whiteList.contains(e.requestOptions.path)) {
      return handler.next(e);
    }
    if (GloablConfig.DEBUG) {
      LoggerUtil().info(
          "[Service-dio] Error found on ${e.requestOptions.method} ${e.requestOptions.uri}");
    }
    if (e.type == DioExceptionType.connectionTimeout) {
      // It occurs when url is opened timeout.
      await SnackBarUtil().error(TextEnum.serverErrorMsg);
    } else if (e.type == DioExceptionType.connectionError) {
      // It occurs when url is opened timeout.
      await SnackBarUtil().error(TextEnum.serverErrorMsg);
    } else if (e.type == DioExceptionType.sendTimeout) {
      // It occurs when url is sent timeout.
      // await SnackBarUtil().error(
      //   'The server is busy, please try again.',
      // );
    } else if (e.type == DioExceptionType.receiveTimeout) {
      //It occurs when receiving timeout
      // await SnackBarUtil().error(
      //   'The server is busy, please try again.',
      // );
    } else if (e.type == DioErrorType.badResponse) {
      // When the server response, but with a incorrect status, such as 404, 503...
      if (e.response?.statusCode == 400) {
        await SnackBarUtil().error(
          e.response?.data['message'],
        );
      } else if (e.response?.statusCode == 401) {
        await SnackBarUtil().error(
          'Your role at this season has been updated. Please log in again.',
        );
        // dio.lock();
        LoggerUtil().error('[Service-dio] logout:${e.response}');
        await AuthStore().logout(force: true);
      } else if (e.response?.statusCode == 403) {
        await SnackBarUtil().error(
          '${e.response?.data['message']}.',
        );
        // await AuthStore().logout(force: true);
      } else if (e.response?.statusCode == 409) {
        await SnackBarUtil().error(e.response?.data['message']);
      } else if (e.response?.statusCode == 500) {
        await SnackBarUtil()
            .error('The server is busy, please try again later');
      } else if (e.response?.statusCode == 404) {
        LoggerUtil().error('[Service-dio] logout:${e.response}');
        // await AuthStore().logout(force: true);
      } else if (e.response?.statusCode == 422) {
        await SnackBarUtil().error(e.response?.data['message']);
      }
    } else if (e.type == DioErrorType.cancel) {
      // When the request is cancelled, dio will throw a error with this type.
      LoggerUtil().error("[Service-dio] 请求取消");
    } else {
      //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
      LoggerUtil().error("[Service-dio] 未知错误:$e");
      if ((e.error?.toString() ?? '')
          .toString()
          .contains("HandshakeException")) {
        // Just a network failure
      } else {
        await SnackBarUtil().error(e.error.toString());
        // AuthStore().logout(force: true);
      }
    }
    return handler.next(e);
  }

  whiteListFilter(String url) => url.endsWith('_allow_anonymous=true');

  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (!whiteList.contains(options.path)) {
      String? token =
          StorageService.to.getString(CacheKeys.token, isWithUser: false);
      if (options.headers['Authorization'] == null && token != null) {
        options.headers["Authorization"] = "Bearer $token";
      }
      options.headers['device-id'] = StorageService.to.getString(
          CacheKeys.deviceUUID,
          isWithUser: false); // 设备id或者udid，保证当前设备唯一值。
      options.headers["platform"] = "iOS";
      options.headers['model'] = StorageService.to
          .getString(CacheKeys.deviceModel, isWithUser: false); // 设备型号
    }
    if (GloablConfig.DEBUG && (options.data is! FormData)) {
      LoggerUtil().info('[Service-dio] url: ${options.path}' +
          ', params: ${jsonEncode(options.queryParameters)}' +
          ', body: ${jsonEncode(options.data)}, Header:${jsonEncode(options.headers)}');
    }

    handler.next(options);
  }

  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    /* LoggerUtil().info('[Service-dio]  ${response.data}'); */
    if (whiteList.contains(response.requestOptions.path)) {
      handler.next(response);
      return;
    }
    if (response.data != null) {
      if (response.data['code'] == 200) {
        if (GloablConfig.DEBUG) LoggerUtil().info(response.data['data']);
        response.data = response.data['data'];
        // 分页数据处理
        if (response.data != null &&
            response.data is Map &&
            response.data['meta'] != null &&
            response.data['items'] != null) {
          response.data = PaginationData.fromJson(response.data);
        }
      } else {
        await SnackBarUtil().error(response.data['message']);
      }
    }
    handler.next(response);
  }
}
