import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:farm_tpf/custom_model/ActionType.dart';
import 'package:farm_tpf/custom_model/CropPlot.dart';
import 'package:farm_tpf/custom_model/Device.dart';
import 'package:farm_tpf/custom_model/EnvironmentParameter.dart';
import 'package:farm_tpf/custom_model/Harvest.dart';
import 'package:farm_tpf/custom_model/LocationUnit.dart';
import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart';
import 'package:farm_tpf/custom_model/Supply.dart';
import 'package:farm_tpf/custom_model/TbCropDTO.dart';
import 'package:farm_tpf/custom_model/UpdateNoti.dart';
import 'package:farm_tpf/custom_model/WaterType.dart';
import 'package:farm_tpf/custom_model/action_form/ActionUIForm.dart';
import 'package:farm_tpf/custom_model/action_form/CommonData.dart';
import 'package:farm_tpf/custom_model/action_form/RequestActivity.dart';
import 'package:farm_tpf/custom_model/user.dart';
import 'package:farm_tpf/custom_model/user_request.dart';
import 'package:farm_tpf/data/api/dio_provider.dart';
import 'package:farm_tpf/data/api/rest_client.dart';
import 'package:farm_tpf/models/PagedResult.dart';
import 'package:farm_tpf/presentation/screens/codes/models/activity_request.dart';
import 'package:farm_tpf/presentation/screens/codes/models/activity_type.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_filter_request.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart';
import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.dart';
import 'package:farm_tpf/presentation/screens/plot/models/crop_filter_request.dart';
import 'package:farm_tpf/presentation/screens/task/models/employee.dart';
import 'package:farm_tpf/presentation/screens/task/models/supply_filter.dart';
import 'package:farm_tpf/presentation/screens/task/models/task_request.dart';
import 'package:farm_tpf/utils/const_common.dart';
import 'package:flutter/material.dart';

import '../../presentation/screens/codes/models/stamp_type.dart';
import '../../presentation/screens/login/models/request_user.dart';
import '../../presentation/screens/login/models/response_user.dart';
import '../../presentation/screens/task/models/task.dart';
import '../../presentation/screens/task/models/task_filter_request.dart';
import '../../presentation/screens/task/models/task_update_request.dart';
import '../api/app_exception.dart';

class Repository {
  final dio = DioProvider();

  Future<void> login(
    Function(ResponseUser) onSuccess,
    Function(String) onError, {
    required RequestUser requestUser,
  }) async {
    try {
      var url = ConstCommon.baseUrl + '/api/authenticate';
      await dio.post(
        url,
        data: {
          'username': requestUser.username ?? '',
          'password': requestUser.password ?? '',
        },
      ).then(
        (value) {
          var res = ResponseUser.fromJson(value.data);
          onSuccess(res);
        },
      ).catchError((error) {
        var dioError = error as DioError;
        var message = dioError.response?.data['Error'];
        onError(message);
      });
    } catch (e) {
      onError('Đã có lỗi xảy ra');
    }
  }

  Future<List<ActionType>> getActionTypes() {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    return client.getActionTypes(options: op);
  }

  Future<List<Harvest>> getHarvests() {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    return client.getHarvests(options: op);
  }

  Future<List<WaterType>> getWaterTypes() {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    return client.getWaterTypes(options: op);
  }

  Future<CropPlot> getPlotDetail(int cropId, {int page = 0, int size = 20}) {
    final client = RestClient(dio);
    return client.getCropDetail(cropId, page: page, size: size);
  }

  Future<CropPlot> getPlotDetailByCode(String cropCode) {
    final client = RestClient(dio);
    return client.getCropDetailByCode(cropCode);
  }

  Future<List<TbCropDTO>> getPlots({int page = 0, int size = 20, String searchString = ''}) {
    final client = RestClient(dio);
    return client.getPlots(page: page, size: size, query: searchString);
  }

  Future<void> updatePlot(TbCropDTO tbCropDTO) {
    final client = RestClient(dio);
    return client.updateCrop(tbCropDTO);
  }

  Future<User> signInWithCredentials(String username, String password) {
    final client = RestClient(dio);
    return client.login(UserRequest(username: username, password: password));
  }

  Future<PagedResult<T>> getInfinityList<T>(String url, {int page = 0, int size = 20}) async {
    var initUrl = "/api/activities/latest-env-by-activity-type/1/2";
    var url = ConstCommon.baseUrl + initUrl + "?page=$page&paged=true&size=$size";
    var response = await dio.get(url);

    final value = PagedResult<T>.fromJson(response.data, getInstanceClass());
    return value;
  }

  Future<List<Supply>> getSupplies(String type, {String query = ""}) async {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    return client.getSupplies(type, query: query, options: op);
  }

  Future<List<Device>> getDeviceForActivity() async {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    return client.getDeviceForActivity(options: op);
  }

  Future<void> updateAllNotification(String status) {
    final client = RestClient(dio);
    return client.updateAllNotification(status);
  }

  Future<void> updateNoti(UpdateNoti updateNoti) {
    final client = RestClient(dio);
    return client.updateNoti(updateNoti);
  }

  Future<NotificationObjectDTO> getNotifications({int page = 0, int size = 20}) async {
    var url = ConstCommon.baseUrl + "/api/notifications-current-user?page=$page&size=$size&sort=sendDate,DESC";
    var response = await dio.get(url);

    final value = NotificationObjectDTO.fromJson(response.data);
    return value;
  }

  Future<List<LocationUnit>> getLocationUnits(
      {required LocationType locationType, int filterId = -1, int page = 0, int size = 500, String query = ''}) {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    var response;
    switch (locationType) {
      case LocationType.province:
        response = client.getProvinces(filterId, options: op, page: page, size: size, query: query);
        break;
      case LocationType.district:
        response = client.getDistricts(filterId, options: op, page: page, size: size, query: query);
        break;
      case LocationType.ward:
        response = client.getWards(filterId, options: op, page: page, size: size, query: query);
        break;
      default:
        response = client.getCountries(query: query);
    }
    return response;
  }

  Object getInstanceClass() {
    var instanceClass;
    if (1 == 1) {
      instanceClass = new TbCropDTO();
    }
    return instanceClass;
  }

  //Action
  Future<void> createAction(Function(dynamic) onSuccess, Function(dynamic) onError,
      {String? apiAddAction, String? paramActivity, String? activityAction, List<String>? filePaths}) async {
    var formData = FormData();
    filePaths?.forEach((f) {
      formData.files.add(MapEntry("images", MultipartFile.fromFileSync(f)));
    });
    formData.fields.add(MapEntry(paramActivity ?? '', activityAction ?? ''));
    try {
      await dio.post("${ConstCommon.baseUrl}/$apiAddAction", data: formData).then((value) {
        onSuccess(value.data);
      });
    } on DioError catch (e) {
      onError(e);
    }
  }

  Future<void> updateAction(Function(dynamic) onSuccess, Function(dynamic) onError,
      {String? apiUpdateAction, String? paramActivity, String? activityAction, List<String>? filePaths}) async {
    var formData = FormData();
    if (filePaths != null) {
      filePaths.forEach((f) {
        formData.files.add(MapEntry("images", MultipartFile.fromFileSync(f)));
      });
    }

    formData.fields.add(MapEntry(paramActivity ?? '', activityAction ?? ''));
    try {
      await dio.post("${ConstCommon.baseUrl}/$apiUpdateAction", data: formData).then((value) {
        onSuccess(value.data);
      });
    } on DioError catch (e) {
      onError(e);
    }
  }

  //Device
  Future<List<Device>> getDevices(String query) {
    final client = RestClient(dio);
    return client.getDevices(query);
  }

  //Environment Parameter
  Future<List<EnvironmentParameter>> getEnvironmentParameters({required int cropId, int page = 0, int size = 20}) {
    final client = RestClient(dio);
    return client.getEnvironmentParameters(cropId, page: page, size: size);
  }

  //NEW IMPLEMENT
  Future<void> allActionTypes(int cropTypeId, Function(List<ActionType>) onSuccess, Function(dynamic) onError) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/listActivityTypesCulture/$cropTypeId';

      final Response<List<dynamic>> _result = await dio.get(url);
      var value = _result.data?.map((dynamic i) => ActionType.fromJson(i as Map<String, dynamic>)).toList();
      onSuccess(value ?? []);
    } on DioError catch (e) {
      onError(e);
    }
  }

  Future<ActionUIForm> getActionUIForm({required int idAction}) {
    final client = RestClient(dio);
    var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
    return client.getActionUIForm(idAction, op);
  }

  Future<List<CommonData>> getCommonData({required String tableSupply, required String condition}) async {
    var url = '${ConstCommon.baseUrl}/api/get-data-from-table-common?tableName=$tableSupply&condition=$condition';

    final Response<List<dynamic>> _result = await dio.get(url);
    var value = _result.data?.map((dynamic i) => CommonData.fromJson(i as Map<String, dynamic>)).toList();
    return value ?? [];
  }

  Future<void> createActionCommon(Function(dynamic) onSuccess, Function(dynamic) onError,
      {required String activityType, required String activityData, List<String>? filePaths}) async {
    var formData = FormData();
    if (filePaths != null) {
      filePaths.forEach((f) {
        formData.files.add(MapEntry('images', MultipartFile.fromFileSync(f)));
      });
    }

    formData.fields.add(MapEntry('tbCommonActivityDTO', activityData));
    try {
      await dio.post("${ConstCommon.baseUrl}/api/create-common-activity/$activityType", data: formData).then((value) {
        onSuccess(value.data);
      });
    } on DioError catch (e) {
      onError(e);
    }
  }

  Future<void> updateActionCommon(Function(dynamic) onSuccess, Function(dynamic) onError,
      {required String activityType, required String activityData, List<String>? filePaths}) async {
    var formData = FormData();
    if (filePaths != null) {
      filePaths.forEach((f) {
        formData.files.add(MapEntry('images', MultipartFile.fromFileSync(f)));
      });
    }

    formData.fields.add(MapEntry('tbCommonActivityDTO', activityData));
    try {
      await dio.post("${ConstCommon.baseUrl}/api/update-common-activity/$activityType", data: formData).then((value) {
        onSuccess(value.data);
      });
    } on DioError catch (e) {
      onError(e);
    }
  }

  Future<RequestActivity> detailCommonAction({required int activityId, required String activityType}) async {
    final client = RestClient(dio);
    return client.getDetailActivityCommon(activityType, activityId);
  }

  // Stamp
  Future<List<Stamp>> stamps({
    int page = 0,
    int size = 20,
    required StampFilterRequest filter,
  }) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes/list?page=$page&size=$size&sort=createdDate,${filter.sort ?? 'asc'}';
      var res = await dio.post(url, data: {
        'status': filter.status,
        'description': filter.description,
      });
      return (res.data as List).map((e) => Stamp.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Future<List<StampType>> stampTypes() async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-example-stamp/dropdown-list';
      var res = await dio.get(
        url,
      );
      return (res.data as List).map((e) => StampType.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Future<void> createStamp(
    Function(dynamic) onSuccess,
    Function(String) onError, {
    required RequestStamp item,
  }) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes';

      await dio.post(url, data: item).then(
        (value) {
          onSuccess(value);
        },
      ).catchError((e) {
        onError(AppException.handleError(e));
      });
    } catch (e) {
      onError(AppException.handleError(e));
    }
  }

  Future<List<ActivityType>> activityTypes() async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-activity-types-dropdown-list-after-harvest';
      var res = await dio.get(
        url,
      );
      return (res.data as List).map((e) => ActivityType.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Future<void> updateActivity(
    Function(dynamic) onSuccess,
    Function(String) onError, {
    required ActivityRequest item,
  }) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes/create/activity';
      var formData = FormData();
      formData.fields.add(MapEntry('code', item.code ?? ''));
      formData.fields.add(MapEntry('activityTypeId', item.activityTypeId?.toString() ?? ''));
      formData.fields.add(MapEntry('executeDate', item.executeDate ?? ''));
      formData.fields.add(MapEntry('description', item.description ?? ''));
      formData.fields.add(MapEntry('location', item.location ?? ''));
      await dio.post(url, data: formData).then(
        (value) {
          onSuccess(value);
        },
      ).catchError((e) {
        onError(AppException.handleError(e));
      });
    } catch (e) {
      onError(AppException.handleError(e));
    }
  }

  Future<Stamp> getStampDetail({required int id}) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes/$id';
      var res = await dio.get(url);
      return Stamp.fromJson(res.data);
    } catch (e) {
      rethrow;
    }
  }

  Future<Stamp> getStampDetailByCode({required String code}) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes-scan-qrCode/$code';
      var res = await dio.get(url);
      return Stamp.fromJson(res.data);
    } catch (e) {
      rethrow;
    }
  }

  Future<StampTimeline> getStampTimeline({required int stampId}) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes-timeline/$stampId';
      var res = await dio.get(url);
      return StampTimeline.fromJson(res.data);
    } catch (e) {
      rethrow;
    }
  }

  Future<void> updateStampStatus(
    Function(dynamic) onSuccess,
    Function(String) onError, {
    required int stampId,
    required String status,
  }) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-codes/update/$stampId/status/$status';

      await dio.put(url).then(
        (value) {
          onSuccess(value);
        },
      ).catchError((e) {
        onError(AppException.handleError(e));
      });
    } catch (e) {
      onError(AppException.handleError(e));
    }
  }

  Future<List<TbCropDTO>> getPlotsWithoutPaging() async {
    try {
      var url = '${ConstCommon.baseUrl}/api/_search/tb-crops';
      var res = await dio.get(
        url,
      );
      return (res.data as List).map((e) => TbCropDTO.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  // Task
  Future<List<Task>> tasks({
    int page = 0,
    int size = 20,
    required TaskFilterRequest filter,
  }) async {
    try {
      // var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/list?page=$page&size=$size&sort=createdDate,${filter.sort ?? 'asc'}';
      var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/list?page=$page&size=$size';
      var res = await dio.post(url, data: {
        // 'status': filter.status,
        "crop_id": filter.cropId,
      });

      return (res.data as List).map((e) => Task.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Future<void> createTask(
    Function(dynamic) onSuccess,
    Function(String) onError, {
    required RequestTask item,
  }) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-todo-lists';

      await dio.post(url, data: item).then(
        (value) {
          onSuccess(value);
        },
      ).catchError((e) {
        onError(AppException.handleError(e));
      });
    } catch (e) {
      onError(AppException.handleError(e));
    }
  }

  Future<void> updateTask(
    Function(dynamic) onSuccess,
    Function(String) onError, {
    required RequestTaskUpdate item,
  }) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/';

      await dio.put(url, data: item).then(
        (value) {
          onSuccess(value);
        },
      ).catchError((e) {
        onError(AppException.handleError(e));
      });
    } catch (e) {
      onError(AppException.handleError(e));
    }
  }

  Future<List<Employee>> getEmployees() async {
    try {
      var url = '${ConstCommon.baseUrl}/api/get-all-users-by-login-info';
      var res = await dio.get(
        url,
      );
      return (res.data as List).map((e) => Employee.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Future<List<SupplyFilter>> getSuppliesFilter() async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-supplies/by-login-info/';
      var res = await dio.get(
        url,
      );
      return (res.data as List).map((e) => SupplyFilter.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  // Crop
  Future<List<TbCropDTO>> crops({
    int page = 0,
    int size = 20,
    required CropFilterRequest filter,
  }) async {
    try {
      // var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/list?page=$page&size=$size&sort=createdDate,${filter.sort ?? 'asc'}';
      var url = '${ConstCommon.baseUrl}/api/tb-crops/list?page=$page&size=$size';
      var res = await dio.post(url, data: {
        'tbSuppliesIds': filter.supplyIds,
      });

      return (res.data as List).map((e) => TbCropDTO.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Future<Task> getTaskDetail({required int id}) async {
    try {
      var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/$id';
      var res = await dio.get(url);
      return Task.fromJson(res.data);
    } catch (e) {
      rethrow;
    }
  }
}
