import 'dart:developer';

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_list_model.dart';
import 'package:mc_common_app/models/appointments_models/schedule_model.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
import 'package:mc_common_app/utils/enums.dart';

abstract class AppointmentRepo {
  Future<List<AppointmentListModel>> getMyAppointmentsForProvider(Map<String, dynamic> map);

  Future<List<AppointmentListModel>> getMyAppointmentsForCustomersByFilters({
    List<String>? providerIdsList,
    List<String>? categoryIdsList,
    List<String>? serviceIdsList,
    List<String>? itemIdsList,
    List<String>? branchIdsList,
  });

  Future<GenericRespModel> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum});

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

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

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

  Future<Services> getAllServices(String branchId);

  Future<GenericRespModel> createSchedule(Map map);

  Future<GenericRespModel> addServicesInSchedule(Map map);

  Future<GenericRespModel> updateSchedule(Map map);

  Future<List<ScheduleData>> getSchedules(String branchId);

  Future<GenericRespModel> updateServicesInSchedule(Map map);

  Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
    required List<String> serviceItemIdsForHome,
    required List<String> serviceItemIdsForWorkshop,
  });

  Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID});

  Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction});
}

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

  @override
  Future<Services> getAllServices(String branchId) async {
    Map<String, dynamic> map = {"ProviderBranchID": branchId};
    String t = appState.getUser.data!.accessToken ?? "";
    return await apiClient.getJsonForObject((json) => Services.fromJson(json), ApiConsts.getServicesOfBranch, token: t, queryParameters: map);
  }

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

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

  @override
  Future<List<ScheduleData>> getSchedules(String branchId) async {
    Map<String, dynamic> map = {"ServiceProviderBranchID": branchId};
    String t = appState.getUser.data!.accessToken ?? "";

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
      (json) => GenericRespModel.fromJson(json),
      ApiConsts.getSchedule,
      token: t,
      queryParameters: map,
    );

    return List.generate(adsGenericModel.data.length, (index) => ScheduleData.fromJson(adsGenericModel.data[index]));
  }

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

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

  @override
  Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
    required List<String> serviceItemIdsForHome,
    required List<String> serviceItemIdsForWorkshop,
  }) async {
    String t = appState.getUser.data!.accessToken ?? "";
    var queryParameters = [];

    if (serviceItemIdsForWorkshop.isNotEmpty) {
      queryParameters.add({
        "appointmentType": 1,
        "ServiceItemIDs": serviceItemIdsForWorkshop,
      });
    }

    if (serviceItemIdsForHome.isNotEmpty) {
      queryParameters.add({
        "appointmentType": 2,
        "ServiceItemIDs": serviceItemIdsForHome,
      });
    }
    GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
      (json) => GenericRespModel.fromJson(json),
      ApiConsts.getServiceItemAppointmentScheduleSlots,
      queryParameters,
      token: t,
    );
    if (adsGenericModel.data == null) {
      return [];
    }
    List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel =
        List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true));
    return serviceAppointmentScheduleModel;
  }

  @override
  Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID}) async {
    String t = appState.getUser.data!.accessToken ?? "";
    int customerId = appState.getUser.data!.userInfo!.customerId ?? 0;

    List<Map<String, dynamic>> mapList = [];
    for (var schedule in schedules) {
      List<int> serviceItemIds = [];
      for (var service in schedule.servicesListInAppointment!) {
        for (var item in service.serviceItems!) {
          serviceItemIds.add(item.id!);
        }
      }

      int slotId = 0;

      int index = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.indexWhere((element) => element.isSelected);

      if (index == -1) {
        index = 0;
      }

      slotId = schedule.selectedCustomTimeDateSlotModel!.availableSlots![index].slotId;

      mapList.add({
        "serviceSlotID": slotId,
        "serviceProviderID": serviceProviderID,
        "customerID": customerId,
        "serviceItemID": serviceItemIds,
        "amountCustomerLocation": schedule.totalLocationCharges.toString(),
        "customerLocLat": (schedule.locationInfoModel!.latitude).toString(),
        "customerLocLong": (schedule.locationInfoModel!.longitude).toString(),
        "customerLocAddress": (schedule.locationInfoModel!.address).toString(),
      });
    }
    log("maplist: ${mapList.toString()}");

    GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
      (json) => GenericRespModel.fromJson(json),
      ApiConsts.serviceProvidersAppointmentCreate,
      mapList,
      token: t,
    );

    return adsGenericModel;
  }

  @override
  Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction}) async {
    String t = appState.getUser.data!.accessToken ?? "";

    final payload = {
      "serviceAppointmentID": serviceAppointmentID,
      "serviceSlotID": serviceSlotID,
      "appointmentScheduleAction": appointmentScheduleAction,
    };

    GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
      (json) => GenericRespModel.fromJson(json),
      ApiConsts.serviceProviderAppointmentRescheduleCancelAppointment,
      payload,
      token: t,
    );

    return adsGenericModel;
  }

  @override
  Future<List<AppointmentListModel>> getMyAppointmentsForProvider(Map<String, dynamic> map) async {
    String t = appState.getUser.data!.accessToken ?? "";

    GenericRespModel genericRespModel = await apiClient.getJsonForObject(
      token: t,
      (json) => GenericRespModel.fromJson(json),
      queryParameters: map,
      ApiConsts.serviceProvidersAppointmentGet,
    );
    List<AppointmentListModel> appointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index]));
    return appointmentList;
  }

  @override
  Future<List<AppointmentListModel>> getMyAppointmentsForCustomersByFilters({
    List<String>? providerIdsList,
    List<String>? categoryIdsList,
    List<String>? serviceIdsList,
    List<String>? itemIdsList,
    List<String>? branchIdsList,
  }) async {
    var params = {
      "customerID": appState.getUser.data!.userInfo!.customerId.toString(),
      "ServiceProviderIDs": providerIdsList ?? [],
      "ProviderBranchIDs": branchIdsList ?? [],
      "ServiceIDs": serviceIdsList ?? [],
      "ServiceItemIDs": itemIdsList ?? [],
      "CategoryIDs": categoryIdsList ?? [],
    };
    GenericRespModel genericRespModel = await apiClient.getJsonForObject(
      token: appState.getUser.data!.accessToken,
      (json) => GenericRespModel.fromJson(json),
      queryParameters: params,
      ApiConsts.customersAppointmentGetByFilters,
    );
    List<AppointmentListModel> appointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index]));
    return appointmentList;
  }

  @override
  Future<GenericRespModel> getAppointmentSlots(Map<String, dynamic> map) async {
    String t = appState.getUser.data!.accessToken ?? "";

    GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
      (json) => GenericRespModel.fromJson(json),
      ApiConsts.getAppointmentSlots,
      token: t,
      queryParameters: map,
    );

    return adsGenericModel;
  }

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

  @override
  Future<GenericRespModel> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum}) async {
    String t = appState.getUser.data!.accessToken ?? "";
    var params = {
      "appointmentID": appointmentId.toString(),
      "appointmentStatusID": appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString(),
    };
    return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateAppointmentStatus, params, token: t);
  }

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