import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/appointments_models/appointment_basic_detail_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_review_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/branch.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/categroy.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/document.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/utils/enums.dart';

abstract class BranchRepo {
  Future<GenericRespModel> createBranch({
    required String branchName,
    required String branchDescription,
    required int cityId,
    required String address,
    required String openTime,
    required String closeTime,
    required double latitude,
    required double longitude,
    required List<BranchPostingImages> imagesList,
  });

  Future<GenericRespModel> updateBranch({
    required int branchId,
    required String branchName,
    required String branchDescription,
    required String openTime,
    required String closeTime,
    required int cityId,
    required String address,
    required String latitude,
    required String longitude,
    required List<BranchPostingImages> imagesList,
    bool isNeedToDelete = true,
  });

  Future<GenericRespModel> deleteBranch({required int branchId});

  Future<Branch> fetchAllBranches();

  Future<Category> fetchBranchCategory();

  Future<Services> fetchServicesByCategoryId({required int serviceCategoryId});

  Future<Services> fetchProviderServices(String branchID, String serviceCategoryId);

  Future<List<ServiceModel>> fetchProviderServicesModelByCategoryIdAndBranchID(String branchID, String serviceCategoryId);

  Future<GenericRespModel> createNewService(List<Map<String, dynamic>> map);

  Future<Document> getServiceProviderDocument(dynamic userId);

  Future<GenericRespModel> serviceProviderDocumentsUpdate(List<DocumentData>? documents);

  Future<ProviderModel> getBranchAndServices();

  Future<List<ProviderBasicDataModel>> getAllProvidersWitheBasicData();

  Future<GenericRespModel> createService(List<Map<String, dynamic>> map);

  Future<GenericRespModel> updateService(List<Map<String, dynamic>> map);

  Future<GenericRespModel> updateServiceStatus({required int branchId, required List<int> serviceIds, required ServiceStatusEnum serviceStatusEnum});

  Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int serviceId});

  Future<GenericRespModel> getMatchedServices(int oldBranchId, int newBranchId, int categoryId);

  Future<GenericRespModel> duplicateItems({required String providerBranchID, required List<int> items});

  Future<GenericRespModel> getAllProviderDealers(Map<String, dynamic> map);

  Future<GenericRespModel> getBranchUsers(Map<String, dynamic> map);

  Future<GenericRespModel> assignDealerToBranch(Map<String, dynamic> map);

  Future<GenericRespModel> removeDealerFromBranch(Map<String, dynamic> map);

  Future<GenericRespModel> addNewServicesInAppointment(Map<String, dynamic> map);

  Future<List<BranchDetailModel>> getAllNearBranchAndServices({required double latitude, required double longitude});

  Future<List<ItemData>> getServiceItems({required int serviceId});

  Future<ProviderProfileModel> getBranchAndServicesByProviderId(int providerId, {double? latitude, double? longitude});

  Future<List<BranchDetailModel>> getBranchesByFilters({
    List<int>? providerIdsList,
    List<int>? categoryIdsList,
    List<int>? serviceIdsList,
    int? distanceKm,
    int? rating,
    double? latitude,
    double? longitude,
  });

  Future<List<BranchDetailModel>> getMyRecentBranchesWithServices();

  Future<List<BranchRatingModel>> getBranchRatings({required int serviceProviderBranchID});

  Future<GenericRespModel> submitBranchRatings({required int serviceProviderBranchID, required String title, required String review, required double ratingNo});

  Future<GenericRespModel> addProviderToFavourite({required int providerID});

  Future<GenericRespModel> removeProviderFromFavourite({required int providerID});

  Future<List<ProviderProfileModel>> getMyFavoriteProviders();
}

class BranchRepoImp implements BranchRepo {
  ApiClient apiClient = injector.get<ApiClient>();
  AppState appState = injector.get<AppState>();

  @override
  Future<GenericRespModel> createBranch({
    required String branchName,
    required String branchDescription,
    required int cityId,
    required String address,
    required String openTime,
    required String closeTime,
    required double latitude,
    required double longitude,
    required List<BranchPostingImages> imagesList,
  }) async {
    List serviceProviderBranchImages = [];
    for (var element in imagesList) {
      var imageMap = {
        "id": element.id ?? 0,
        "imageName": element.imageName,
        "imageUrl": element.imageUrl,
        "imageStr": element.imageStr,
      };
      serviceProviderBranchImages.add(imageMap);
    }

    var postParams = {
      "serviceProviderID": AppState().getUser.data?.userInfo?.providerId ?? "",
      "branchName": branchName,
      "branchDescription": branchDescription,
      "cityID": cityId.toString(),
      "address": address,
      "openTime": openTime,
      "closeTime": closeTime,
      "latitude": latitude,
      "longitude": longitude,
      "isActive": true,
      "serviceProviderBranchImages": serviceProviderBranchImages,
    };
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.createProviderBranch, postParams, token: t);
  }

  @override
  Future<Branch> fetchAllBranches() async {
    var postParams = {"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""};
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => Branch.fromJson(json), ApiConsts.serviceProviderBranchGet, queryParameters: postParams, token: t);
  }

  @override
  Future<Category> fetchBranchCategory() async {
    var postParams = {"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""};
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => Category.fromJson(json), ApiConsts.serviceCategoryGet, queryParameters: postParams, token: t);
  }

  @override
  Future<Services> fetchServicesByCategoryId({required int serviceCategoryId}) async {
    var postParams = {"ServiceCategoryID": serviceCategoryId.toString()};
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => Services.fromJson(json), ApiConsts.servicesGet, queryParameters: serviceCategoryId.toString() != "-1" ? postParams : null, token: t);
  }

  @override
  Future<GenericRespModel> createNewService(List<Map<String, dynamic>> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceCreate, map, token: t);
  }

  @override
  Future<Document> getServiceProviderDocument(dynamic userId) async {
    var queryParameters = {
      "ServiceProviderID": userId.toString(),
    };
    String? token = AppState().getUser.data?.accessToken;
    debugPrint(token);
    return await injector
        .get<ApiClient>()
        .getJsonForObject((json) => Document.fromJson(json), ApiConsts.fetProviderDocument, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? "");
  }

  @override
  Future<GenericRespModel> serviceProviderDocumentsUpdate(List<DocumentData>? documents) async {
    List<Map<String, dynamic>> map = [];
    for (int i = 0; i < documents!.length; i++) {
      if (documents[i].document != null) {
        var postParams = {
          "id": documents[i].id,
          "serviceProviderID": documents[i].serviceProviderId,
          "documentID": documents[i].documentId,
          "documentExt": documents[i].fileExt,
          "documentImage": documents[i].document,
          "isActive": true,
        };
        map.add(postParams);
      }
    }
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderDocumentUpdate, map, token: t);
  }

  @override
  Future<ProviderModel> getBranchAndServices() async {
    var postParams = {"serviceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""};
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => ProviderModel.fromJson(json), ApiConsts.branchesAndServices, queryParameters: postParams, token: t);
  }

  @override
  Future<List<ProviderBasicDataModel>> getAllProvidersWitheBasicData() async {
    String t = AppState().getUser.data!.accessToken ?? "";

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
      token: t,
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.serviceProviderDDLGet,
    );
    List<ProviderBasicDataModel> providersList = List.generate(adsGenericModel.data.length, (index) => ProviderBasicDataModel.fromJson(adsGenericModel.data[index]));
    return providersList;
  }

  @override
  Future<GenericRespModel> updateBranch({
    required int branchId,
    required String branchName,
    required String branchDescription,
    required String openTime,
    required String closeTime,
    required int cityId,
    required String address,
    required String latitude,
    required String longitude,
    required List<BranchPostingImages> imagesList,
    bool isNeedToDelete = true,
  }) async {
    List serviceProviderBranchImages = [];
    for (var element in imagesList) {
      var imageMap = {
        "id": element.id ?? 0,
        "imageName": element.imageName,
        "imageUrl": element.imageUrl,
        "imageStr": element.imageStr,
      };
      serviceProviderBranchImages.add(imageMap);
    }

    String lat = "0",
        long = "0";
    try {
      lat = latitude.toString().substring(0, 9);
      long = longitude.toString().substring(0, 9);
    } catch (e) {}
    var postParams = {
      "id": branchId,
      "serviceProviderID": AppState().getUser.data?.userInfo?.providerId ?? "",
      "branchName": branchName,
      "branchDescription": branchDescription,
      "openTime": openTime,
      "closeTime": closeTime,
      "cityID": cityId,
      "address": address,
      "latitude": lat,
      "longitude": long,
      "serviceProviderBranchImages": serviceProviderBranchImages,
      "isActive": isNeedToDelete
    };
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateProviderBranch, postParams, token: t);
  }

  @override
  Future<GenericRespModel> deleteBranch({required int branchId}) async {
    var postParams = {
      "id": branchId,
    };
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.deleteProviderBranch, postParams, token: t);
  }

  @override
  Future<GenericRespModel> createService(List<Map<String, dynamic>> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceCreate, map, token: t);
  }

  @override
  Future<GenericRespModel> updateService(List<Map<String, dynamic>> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceUpdate, map, token: t);
  }

  @override
  Future<GenericRespModel> updateServiceStatus({required int branchId, required List<int> serviceIds, required ServiceStatusEnum serviceStatusEnum}) async {
    List<String> ids = [];

    for (var id in serviceIds) {
      ids.add(id.toString());
    }

    int providerID = AppState().getUser.data!.userInfo!.providerId;
    var map = {
      "ServiceProviderID": providerID.toString(),
      "ProviderBranchID": branchId.toString(),
      "ProviderServiceIDs": ids,
      "Status": serviceStatusEnum.getIdFromServiceStatusEnum().toString(),
    };
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceStatusUpdate, queryParameters: map, token: t);
  }

  @override
  Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}) async {
    var map = {
      "ProviderBranchID": branchId.toString(),
      "ServiceProviderServiceID": serviceId.toString(),
    };

    String t = AppState().getUser.data!.accessToken ?? "";
    GenericRespModel genericRespModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.serviceProviderAppointmentGetByCategoryOrService,
      queryParameters: map,
      token: t,
    );

    List<AppointmentBasicDetailsModel> basicAppointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentBasicDetailsModel.fromJson(genericRespModel.data[index]));

    return basicAppointmentList;
  }

  @override
  Future<GenericRespModel> getMatchedServices(int oldBranchId, int newBranchId, int categoryId) async {
    var postParams = {
      "ProviderBranchIDExisted": oldBranchId.toString(),
      "ProviderBranchIDNew": newBranchId.toString(),
      "CategoryID": categoryId.toString(),
    };
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.getMatchedServices, queryParameters: postParams, token: t);
  }

  @override
  Future<GenericRespModel> duplicateItems({required String providerBranchID, required List<int> items}) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    Map<String, dynamic> map = {
      "providerBranchID": providerBranchID,
      "serviceItems": items,
    };
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.duplicateItems, map, token: t);
  }

  @override
  Future<GenericRespModel> getAllProviderDealers(Map<String, dynamic> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.getAllProviderDealers, queryParameters: map, token: t);
  }

  @override
  Future<GenericRespModel> getBranchUsers(Map<String, dynamic> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.getBranchUser, queryParameters: map, token: t);
  }

  @override
  Future<GenericRespModel> assignDealerToBranch(Map<String, dynamic> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.assignDealerToBranch, map, token: t);
  }

  @override
  Future<GenericRespModel> removeDealerFromBranch(Map<String, dynamic> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.removeDealerFromBranch, map, token: t);
  }

  @override
  Future<Services> fetchProviderServices(String branchID, String serviceCategoryId) async {
    var postParams = {
      "ServiceCategoryID": serviceCategoryId,
      "ProviderBranchID": branchID,
    };
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => Services.fromJson(json), ApiConsts.getProviderServices, queryParameters: postParams, token: t);
  }

  @override
  Future<List<ServiceModel>> fetchProviderServicesModelByCategoryIdAndBranchID(String branchID, String serviceCategoryId) async {
    var queryParameters = {
      "ID": branchID,
      "ServiceCategoryIDs": [serviceCategoryId],
    };
    String t = AppState().getUser.data!.accessToken ?? "";

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.getAllNearBranches,
      token: appState.getUser.data!.accessToken,
      queryParameters: queryParameters,
    );
    List<BranchDetailModel> nearBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index]));
    if (nearBranches.isNotEmpty) {
      List<ServiceModel> services = nearBranches.first.branchServices ?? [];
      return services;
    }
    return [];
  }

  @override
  Future<GenericRespModel> addNewServicesInAppointment(Map<String, dynamic> map) async {
    String t = AppState().getUser.data!.accessToken ?? "";
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.addNewServicesInAppointment, map, token: t);
  }

  @override
  Future<List<BranchDetailModel>> getAllNearBranchAndServices({required double latitude, required double longitude}) async {
    var queryParameters = {
      "isActive": "true",
      "Status": "3",
      "latitude": latitude.toString(),
      "longitude": longitude.toString(),
    };
    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.getAllNearBranches,
      token: appState.getUser.data!.accessToken,
      queryParameters: queryParameters,
    );
    List<BranchDetailModel> nearBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index]));
    return nearBranches;
  }

  @override
  Future<List<BranchDetailModel>> getMyRecentBranchesWithServices() async {
    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.getMyRecentBranches,
      token: appState.getUser.data!.accessToken,
    );
    List<BranchDetailModel> recentBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index]));
    return recentBranches;
  }

  @override
  Future<List<ItemData>> getServiceItems({required int serviceId}) async {
    var queryParameters = {
      "ServiceProviderServiceID": serviceId.toString(),
    };

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.getServiceItems,
      token: appState.getUser.data!.accessToken,
      queryParameters: serviceId.toString() != "-1" ? queryParameters : null,
    );
    List<ItemData> serviceItems = List.generate(adsGenericModel.data.length, (index) => ItemData.fromJson(adsGenericModel.data[index]));
    return serviceItems;
  }

  @override
  Future<ProviderProfileModel> getBranchAndServicesByProviderId(int providerId, {double? latitude, double? longitude}) async {
    var postParams = {
      "serviceProviderID": providerId.toString(),
    };

    if (latitude != null && longitude != null) {
      postParams.addAll({"Longitude": "$longitude"});
      postParams.addAll({"Latitude": "$latitude"});
    }
    GenericRespModel adsGenericModel =
    await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);

    return ProviderProfileModel.fromJson(adsGenericModel.data);
  }

  @override
  Future<List<BranchDetailModel>> getBranchesByFilters({
    List<int>? providerIdsList,
    List<int>? categoryIdsList,
    List<int>? serviceIdsList,
    int? distanceKm,
    int? rating,
    double? latitude,
    double? longitude,
  }) async {
    List<String> providerIds = [];
    if (providerIdsList != null) {
      for (var element in providerIdsList) {
        providerIds.add(element.toString());
      }
    }
    List<String> categoryIds = [];
    if (categoryIdsList != null) {
      for (var element in categoryIdsList) {
        categoryIds.add(element.toString());
      }
    }
    List<String> serviceIds = [];
    if (serviceIdsList != null) {
      for (var element in serviceIdsList) {
        serviceIds.add(element.toString());
      }
    }
    var postParams = {
      "ServiceProviderIDs": providerIds,
      "ServiceCategoryIDs": categoryIds,
      "ServiceIDs": serviceIds,
      "DistanceByKM": "${distanceKm ?? 0}",
      "Rating": "${rating ?? 0}",
      "Latitude": latitude.toString(),
      "Longitude": longitude.toString(),
      "Status": "3",
    };

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.getAllNearBranches,
      token: appState.getUser.data!.accessToken,
      queryParameters: postParams,
    );
    List<BranchDetailModel> nearBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index]));
    return nearBranches;
  }

  @override
  Future<List<BranchRatingModel>> getBranchRatings({required int serviceProviderBranchID}) async {
    var postParams = {"ServiceProviderBranchID": serviceProviderBranchID.toString()};

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.getBranchRatings,
      token: appState.getUser.data!.accessToken,
      queryParameters: postParams,
    );
    List<BranchRatingModel> branchRatings = List.generate(adsGenericModel.data.length, (index) => BranchRatingModel.fromJson(adsGenericModel.data[index]));
    return branchRatings;
  }

  @override
  Future<GenericRespModel> submitBranchRatings({required int serviceProviderBranchID, required String title, required String review, required double ratingNo}) async {
    final customerID = appState.getUser.data!.userInfo!.customerId;
    final parameters = {"title": title, "review": review, "ratNo": ratingNo, "serviceProviderBranchID": serviceProviderBranchID, "customerID": "$customerID"};
    GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.createBranchRatings,
      parameters,
      token: appState.getUser.data!.accessToken,
    );
    return adsGenericModel;
  }

  @override
  Future<GenericRespModel> addProviderToFavourite({required int providerID}) async {
    final customerID = appState.getUser.data!.userInfo!.customerId;
    final parameters = {
      "providerID": providerID.toString(),
      "customerID": customerID.toString(),
    };
    GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.favouriteServiceProviderCreate,
      parameters,
      token: appState.getUser.data!.accessToken,
    );
    return adsGenericModel;
  }

  @override
  Future<GenericRespModel> removeProviderFromFavourite({required int providerID}) async {
    final parameters = {"id": providerID.toString()};
    GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.unFavouriteServiceProvider,
      parameters,
      token: appState.getUser.data!.accessToken,
    );
    return adsGenericModel;
  }

  @override
  Future<List<ProviderProfileModel>> getMyFavoriteProviders() async {
    final customerID = appState.getUser.data!.userInfo!.customerId;

    var postParams = {"customerID": customerID.toString()};

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
          (json) => GenericRespModel.fromJson(json),
      ApiConsts.favouriteServiceProviderGet,
      token: appState.getUser.data!.accessToken,
      queryParameters: postParams,
    );
    List<ProviderProfileModel> favProviders = List.generate(adsGenericModel.data.length, (index) => ProviderProfileModel.fromJson(adsGenericModel.data[index]));
    return favProviders;
  }
}
