package com.bonus.canteen.core.order.common.query;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.bonus.common.houqin.constant.DelFlagEnum;
import com.bonus.common.houqin.constant.LeConstants;
import com.bonus.canteen.core.allocation.canteen.model.AllocCanteen;
import com.bonus.canteen.core.allocation.canteen.model.AllocStall;
import com.bonus.canteen.core.allocation.canteen.service.AllocCanteenService;
import com.bonus.canteen.core.allocation.canteen.service.AllocStallService;
import com.bonus.canteen.core.common.enums.AllocCanteenTypeEnum;
import com.bonus.canteen.core.common.enums.AllocMealtimeTypeEnum;
import com.bonus.canteen.core.common.utils.SpringContextHolder;
import com.bonus.canteen.core.order.common.constants.OrderTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class OrderQueryHelper {
   private static final Logger log = LoggerFactory.getLogger(OrderQueryHelper.class);
   private static final int MAX_IN_PARAM_SIZE = 1000;

   public static boolean initQueryParam(OrderQueryParam param, OrderIndexType... preferIndexTypes) {
      log.info("[订单索引优化] 初始化查询参数", param, preferIndexTypes);
      List<OrderIndexType> preferOrderIndexList = CollUtil.toList(preferIndexTypes);
      List<OrderIndexType> allMatchIndexList = CollUtil.toList(new OrderIndexType[]{OrderIndexType.MAC_ORDER_ID, OrderIndexType.CUST_ID_ORDER_DATE, OrderIndexType.MACHINE_SN_ORDER_DATE, OrderIndexType.ROOM_ID_ORDER_DATE, OrderIndexType.ORDER_TYPE_ORDER_DATE, OrderIndexType.CANTEEN_ID_STALL_ID_MEALTIME_ORDER_TYPE_ORDER_DATE});
      List<OrderIndexType> allFillIndexList = CollUtil.toList(new OrderIndexType[]{OrderIndexType.CUST_ID_ORDER_DATE, OrderIndexType.MACHINE_SN_ORDER_DATE, OrderIndexType.ROOM_ID_ORDER_DATE, OrderIndexType.ORDER_TYPE_ORDER_DATE, OrderIndexType.CANTEEN_ID_STALL_ID_MEALTIME_ORDER_TYPE_ORDER_DATE});
      if (CollUtil.isNotEmpty(preferOrderIndexList)) {
         allMatchIndexList.removeAll(preferOrderIndexList);
         allFillIndexList.removeAll(preferOrderIndexList);
         if (matchIndex(param, (List)preferOrderIndexList) || fillIndex(param, (List)preferOrderIndexList)) {
            return true;
         }
      }

      return matchIndex(param, (List)allMatchIndexList) || fillIndex(param, (List)allFillIndexList);
   }

   public static boolean initQueryDate(OrderQueryParam param, Integer... daysRange) {
      if (!param.containsAvailableOrderDate()) {
         LocalDate now = LocalDate.now();
         param.setStartDate(ArrayUtil.isNotEmpty(daysRange) ? now.plusDays((long)daysRange[0]) : defaultMinDateForQuery());
         param.setEndDate(ArrayUtil.isNotEmpty(daysRange) ? now.plusDays((long)daysRange[1]) : defaultMaxDateForQuery());
         return param.containsAvailableOrderDate();
      } else {
         return false;
      }
   }

   private static boolean matchIndex(OrderQueryParam param, List<OrderIndexType> indexTypes) {
      if (CollUtil.isEmpty(indexTypes)) {
         return false;
      } else {
         Iterator var2 = indexTypes.iterator();

         OrderIndexType indexType;
         do {
            if (!var2.hasNext()) {
               log.info("[订单索引优化] matchIndex 未命中");
               return false;
            }

            indexType = (OrderIndexType)var2.next();
         } while(!matchIndex(param, indexType));

         log.info("[订单索引优化] matchIndex {}", indexType);
         return true;
      }
   }

   private static boolean fillIndex(OrderQueryParam param, List<OrderIndexType> indexTypes) {
      if (CollUtil.isEmpty(indexTypes)) {
         return false;
      } else {
         Iterator var2 = indexTypes.iterator();

         OrderIndexType indexType;
         do {
            if (!var2.hasNext()) {
               log.info("[订单索引优化] fillIndex 未命中");
               return false;
            }

            indexType = (OrderIndexType)var2.next();
         } while(!fillIndex(param, indexType));

         log.info("[订单索引优化] fillIndex {}", indexType);
         return true;
      }
   }

   private static boolean matchIndex(OrderQueryParam param, OrderIndexType indexType) {
      switch (indexType) {
         case MAC_ORDER_ID:
            return matchIndexMacOrderId(param);
         case CUST_ID_ORDER_DATE:
            return matchIndexCustIdOrderDate(param);
         case MACHINE_SN_ORDER_DATE:
            return matchIndexMachineSnOrderDate(param);
         case ROOM_ID_ORDER_DATE:
            return matchIndexRoomIdOrderDate(param);
         case CANTEEN_ID_STALL_ID_MEALTIME_ORDER_TYPE_ORDER_DATE:
            return matchIndexCanteenIdStallIdMealtimeOrderTypeOrderDate(param);
         case ORDER_TYPE_ORDER_DATE:
            return matchIndexOrderTypeOrderDate(param);
         case ORDER_TIME:
            return matchIndexOrderTime(param);
         case PAY_TIME:
            return matchIndexPayTime(param);
         default:
            return false;
      }
   }

   private static boolean fillIndex(OrderQueryParam param, OrderIndexType indexType) {
      switch (indexType) {
         case CUST_ID_ORDER_DATE:
            return fillIndexCustIdOrderDate(param);
         case MACHINE_SN_ORDER_DATE:
            return fillIndexMachineSnOrderDate(param);
         case ROOM_ID_ORDER_DATE:
            return fillIndexRoomIdOrderDate(param);
         case CANTEEN_ID_STALL_ID_MEALTIME_ORDER_TYPE_ORDER_DATE:
            return fillIndexCanteenIdStallIdMealtimeOrderTypeOrderDate(param);
         case ORDER_TYPE_ORDER_DATE:
            return fillIndexOrderTypeOrderDate(param);
         default:
            return false;
      }
   }

   private static boolean matchIndexMacOrderId(OrderQueryParam param) {
      return param.containsAvailableMacOrderId();
   }

   private static boolean matchIndexCustIdOrderDate(OrderQueryParam param) {
      return param.containsAvailableCustId() && param.containsAvailableOrderDate();
   }

   private static boolean matchIndexRoomIdOrderDate(OrderQueryParam param) {
      return param.containsAvailableRoomId() && param.containsAvailableOrderDate();
   }

   private static boolean matchIndexMachineSnOrderDate(OrderQueryParam param) {
      return param.containsAvailableMachineSn() && param.containsAvailableOrderDate();
   }

   private static boolean matchIndexOrderTime(OrderQueryParam param) {
      return param.containsAvailableOrderTime();
   }

   private static boolean matchIndexPayTime(OrderQueryParam param) {
      return param.containsAvailablePayTime();
   }

   private static boolean matchIndexCanteenIdStallIdMealtimeOrderTypeOrderDate(OrderQueryParam param) {
      return param.containsAvailableCanteenId() && param.containsAvailableStallId() && param.containsAvailableMealtimeType() && param.containsAvailableOrderType() && param.containsAvailableOrderDate();
   }

   private static boolean matchIndexOrderTypeOrderDate(OrderQueryParam param) {
      return param.containsAvailableOrderType() && param.containsAvailableOrderDate();
   }

   private static boolean fillIndexCustIdOrderDate(OrderQueryParam param) {
      if (param.containsAvailableCustId() && !param.containsAvailableOrderDate()) {
         param.setStartDate(defaultMinDateForQuery());
         param.setEndDate(defaultMaxDateForQuery());
         return param.containsAvailableOrderDate();
      } else {
         return false;
      }
   }

   private static boolean fillIndexMachineSnOrderDate(OrderQueryParam param) {
      if (param.containsAvailableMachineSn() && !param.containsAvailableOrderDate()) {
         param.setStartDate(defaultMinDateForQuery());
         param.setEndDate(defaultMaxDateForQuery());
         return param.containsAvailableOrderDate();
      } else {
         return false;
      }
   }

   private static boolean fillIndexRoomIdOrderDate(OrderQueryParam param) {
      if (param.containsAvailableRoomId() && !param.containsAvailableOrderDate()) {
         param.setStartDate(defaultMinDateForQuery());
         param.setEndDate(defaultMaxDateForQuery());
         return param.containsAvailableOrderDate();
      } else {
         return false;
      }
   }

   private static boolean fillIndexOrderTypeOrderDate(OrderQueryParam param) {
      if (param.containsAvailableOrderType() && !param.containsAvailableOrderDate()) {
         param.setStartDate(defaultMinDateForQuery());
         param.setEndDate(defaultMaxDateForQuery());
         return param.containsAvailableOrderDate();
      } else {
         return false;
      }
   }

   private static boolean fillIndexCanteenIdStallIdMealtimeOrderTypeOrderDate(OrderQueryParam param) {
      if (!param.containsAvailableOrderDate()) {
         return false;
      } else if (!param.containsAvailableCanteenId() && !param.containsAvailableStallId() && !param.containsAvailableMealtimeType()) {
         return false;
      } else if (param.availableOrderTypeList().stream().noneMatch((s) -> {
         return OrderTypeEnum.isCanteenCategory(s) || OrderTypeEnum.isShopCategory(s);
      })) {
         return false;
      } else {
         ArrayList stallIds;
         if (!param.containsAvailableCanteenId()) {
            stallIds = CollUtil.newArrayList(new Long[0]);
            if (param.availableOrderTypeList().stream().anyMatch(OrderTypeEnum::isShopCategory)) {
               stallIds.addAll(defaultSupermarketIds());
            }

            if (param.availableOrderTypeList().stream().anyMatch(OrderTypeEnum::isCanteenCategory)) {
               stallIds.addAll(defaultCanteenIds());
            }

            param.setCanteenIdList(stallIds);
            if (!param.containsAvailableCanteenId()) {
               return false;
            }
         }

         if (!param.containsAvailableStallId()) {
            stallIds = CollUtil.newArrayList(new Long[0]);
            if (param.availableOrderTypeList().stream().anyMatch(OrderTypeEnum::isShopCategory)) {
               stallIds.add(LeConstants.DATA_DEFAULT_LONG);
            }

            if (param.availableOrderTypeList().stream().anyMatch(OrderTypeEnum::isCanteenCategory)) {
               stallIds.addAll(defaultStallIds(param.availableCanteenIdList()));
            }

            param.setStallIdList(stallIds);
            if (!param.containsAvailableStallId()) {
               return false;
            }
         }

         if (!param.containsAvailableMealtimeType()) {
            param.setMealtimeTypeList(CollUtil.newArrayList(new Integer[0]));
            if (param.availableOrderTypeList().stream().anyMatch(OrderTypeEnum::isShopCategory)) {
               param.getMealtimeTypeList().add(LeConstants.DATA_DEFAULT_INTEGER);
            }

            if (param.availableOrderTypeList().stream().anyMatch(OrderTypeEnum::isCanteenCategory)) {
               param.getMealtimeTypeList().addAll(defaultMealtimes());
            }

            if (!param.containsAvailableStallId()) {
               return false;
            }
         }

         if (!param.containsAvailableOrderType()) {
            param.setOrderTypeList(OrderTypeEnum.allTypeList());
            if (!param.containsAvailableOrderType()) {
               return false;
            }
         }

         return true;
      }
   }

   private static LocalDate defaultMinDateForQuery() {
      return LocalDate.now().minusYears(1L);
   }

   private static LocalDate defaultMaxDateForQuery() {
      return LocalDate.now().plusMonths(1L);
   }

   public static List<Long> defaultCanteenIds() {
      AllocCanteenService allocCanteenService = (AllocCanteenService)SpringContextHolder.getBean(AllocCanteenService.class);
      List<Long> ids = (List)allocCanteenService.list().stream().filter((s) -> {
         return AllocCanteenTypeEnum.CANTEEN.getKey().equals(s.getCanteenType());
      }).map(AllocCanteen::getCanteenId).collect(Collectors.toList());
      return (List)(ids.size() > 1000 ? CollUtil.newArrayList(new Long[0]) : ids);
   }

   public static List<Long> defaultSupermarketIds() {
      AllocCanteenService allocCanteenService = (AllocCanteenService)SpringContextHolder.getBean(AllocCanteenService.class);
      List<Long> ids = (List)allocCanteenService.list().stream().filter((s) -> {
         return AllocCanteenTypeEnum.SUPERMARKET.getKey().equals(s.getCanteenType());
      }).map(AllocCanteen::getCanteenId).collect(Collectors.toList());
      return (List)(ids.size() > 1000 ? CollUtil.newArrayList(new Long[0]) : ids);
   }

   public static List<Long> defaultStallIds(List<Long> canteenIds) {
      AllocStallService allocStallService = (AllocStallService) SpringContextHolder.getBean(AllocStallService.class);
      List<Long> stallIds = (List)allocStallService.list().stream().filter((s) -> {
         return DelFlagEnum.DEL_FALSE.key().equals(s.getIfDel()) && canteenIds.contains(s.getCanteenId());
      }).map(AllocStall::getStallId).collect(Collectors.toList());
      stallIds.add(LeConstants.DATA_DEFAULT_LONG);
      return (List)(stallIds.size() > 1000 ? CollUtil.newArrayList(new Long[0]) : stallIds);
   }

   public static List<Integer> defaultMealtimes() {
      List<Integer> list = CollUtil.newArrayList(AllocMealtimeTypeEnum.allTypeList());
      list.add(LeConstants.DATA_DEFAULT_INTEGER);
      return list;
   }
}
