package com.bonus.canteen.core.marketing.v2.rule.price.handler.limitmaxcount.extension.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.canteen.core.marketing.v2.constants.MarketDeviceTreeEnum;
import com.bonus.canteen.core.marketing.v2.constants.MarketRangeTypeEnum;
import com.bonus.canteen.core.marketing.v2.constants.MarketSceneTypeEnum;
import com.bonus.canteen.core.marketing.v2.model.MarketRuleRange;
import com.bonus.canteen.core.marketing.v2.rule.price.dto.RulePriceLimitDTO;
import com.bonus.canteen.core.marketing.v2.rule.price.dto.RulePriceResultOrderDTO;
import com.bonus.canteen.core.marketing.v2.rule.price.handler.limitmaxcount.dto.PriceLimitMaxCountDTO;
import com.bonus.canteen.core.marketing.v2.rule.price.handler.limitmaxcount.extension.PriceLimitMaxCountHandlerExtension;
import com.bonus.canteen.core.marketing.v2.service.MarketRuleRangeService;
import com.bonus.canteen.core.marketing.vo.MarketRuleVO;
import com.bonus.canteen.core.order.api.OrderMarketingApi;
import com.bonus.canteen.core.order.common.constants.OrderRefundStateEnum;
import com.bonus.canteen.core.order.common.constants.OrderTypeEnum;
import com.bonus.canteen.core.order.common.model.OrderInfo;
import com.bonus.canteen.core.pay.common.constants.PayStateEnum;
import com.bonus.common.houqin.i18n.I18n;
import com.bonus.canteen.core.marketing.v2.util.MarketUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Service
public class DefaultLimitMaxCountHandlerImpl implements PriceLimitMaxCountHandlerExtension {
   private static final Logger log = LoggerFactory.getLogger(DefaultLimitMaxCountHandlerImpl.class);
   @Autowired
   private MarketRuleRangeService rangeService;
   @Autowired
   private OrderMarketingApi orderMarketingApi;

   public void handle(RulePriceResultOrderDTO order, MarketRuleVO rule, List<RulePriceResultOrderDTO> orderResults) {
      PriceLimitMaxCountDTO item = (PriceLimitMaxCountDTO)JSONObject.parseObject(rule.getRuleInfo(), PriceLimitMaxCountDTO.class);
      List<MarketRuleRange> rangeList = this.rangeService.getRangeByRuleId(rule.getRuleId());
      List<Integer> mealtimeTypeList = this.rangeService.filterRangeDetailByType(rangeList, MarketRangeTypeEnum.RANGE_MEALTIME);
      List<OrderInfo> orders = this.getOrders(order, item.getLimitType(), mealtimeTypeList, rule.getSceneType());
      List<String> customDeviceList = this.rangeService.filterRangeDetailByType(rangeList, MarketRangeTypeEnum.RANGE_DEVICE);
      long oldCount = orders.stream().filter((e) -> {
         return MarketUtil.isValidOrder(customDeviceList, e);
      }).count();
      log.info("【计价规则】历史次数：{}", oldCount);
      Integer nowCount = this.getNowTradeCount(order, mealtimeTypeList, customDeviceList, item.getLimitType(), orderResults);
      log.info("【计价规则】本次次数：{}", nowCount);
      Integer totalCount = NumberUtil.add(new String[]{Long.toString(oldCount), nowCount.toString()}).intValue();
      log.info("【计价规则】合计次数：{}", totalCount);
      if (totalCount.compareTo(item.getMaxCount()) > 0) {
         throw new ServiceException(I18n.getMessage("market_price_over_max_count", new Object[0]));
      }
   }

   protected Integer getNowTradeCount(RulePriceResultOrderDTO order, List<Integer> mealtimeTypeList, List<String> customDeviceList, Integer limitType, List<RulePriceResultOrderDTO> orderResults) {
      return MarketUtil.getNowTrade(order, mealtimeTypeList, customDeviceList, limitType, orderResults).size();
   }

   protected List<OrderInfo> getOrders(RulePriceResultOrderDTO order, Integer limitType, List<Integer> mealtimeTypeList, Integer sceneType) {
      RulePriceLimitDTO limitDTO = new RulePriceLimitDTO(order, limitType, mealtimeTypeList);
      List<Integer> orderTypes = MarketSceneTypeEnum.getOrderTypeBySceneType(sceneType);
      return (List)Optional.ofNullable(this.orderMarketingApi.listOrder(order.getCustId(), limitDTO.getBeginDate(), limitDTO.getEndDate(), limitDTO.getMealtimeTypeList(), orderTypes)).orElse(new ArrayList());
   }

   public static boolean isValidOrder(List<String> customDeviceList, OrderInfo e) {
      return (Objects.equals(e.getPayState(), PayStateEnum.PAY_SUCC.getKey()) || Objects.equals(e.getPayState(), PayStateEnum.PART_PAY.getKey())) && !OrderRefundStateEnum.FINISH.getKey().equals(e.getOrderRefundState()) && (CollUtil.isEmpty(customDeviceList) || CollUtil.contains(customDeviceList, formatDeviceSn(e.getOrderType(), e.getCanteenId(), e.getStallId(), e.getMachineSn())));
   }

   public static String formatDeviceSn(Integer orderType, Long canteenId, Long stallId, String deviceSn) {
      OrderTypeEnum orderTypeEnum = OrderTypeEnum.getEnumByKey(orderType);
      if (Objects.isNull(orderTypeEnum)) {
         return deviceSn;
      } else {
         String finalDeviceSn;
         switch (orderTypeEnum) {
            case CURR_MEAL:
            case RESERVE_MEAL:
            case BOOK_MEAL:
            case BOOK_MEAL_3DAY:
            case BOOK_MEAL_7DAY:
            case MOBILE_SCAN:
               finalDeviceSn = formatMobile(canteenId, stallId);
               break;
            case SHOP:
            case RESERVE_SHOP:
            case MOBILE_SCAN_SHOP:
               finalDeviceSn = formatMarket(canteenId, deviceSn);
               break;
            case DEDUCT:
               finalDeviceSn = CharSequenceUtil.isBlank(deviceSn) ? formatMobile(canteenId, stallId) : formatDevice(canteenId, stallId, deviceSn);
               break;
            case BUFFET:
               finalDeviceSn = formatBuffet(canteenId, stallId);
               break;
            default:
               finalDeviceSn = formatDevice(canteenId, stallId, deviceSn);
         }

         return finalDeviceSn;
      }
   }

   public static String formatMarket(Long canteenId, String deviceSn) {
      return CharSequenceUtil.isBlank(deviceSn) ? CharSequenceUtil.concat(true, new CharSequence[]{NumberUtil.toStr(canteenId, ""), "&&", MarketDeviceTreeEnum.ONLINE_SUPERMARKET.getDesc()}) : CharSequenceUtil.concat(true, new CharSequence[]{NumberUtil.toStr(canteenId, ""), "&&", MarketDeviceTreeEnum.OFFLINE_SUPERMARKET.getDesc()});
   }

   public static String formatDevice(Long canteenId, Long stallId, String deviceSn) {
      return CharSequenceUtil.concat(true, new CharSequence[]{NumberUtil.toStr(canteenId, ""), "&", CharSequenceUtil.equals(NumberUtil.toStr(stallId, ""), "-1") ? "" : NumberUtil.toStr(stallId, ""), "&", deviceSn});
   }

   public static String formatMobile(Long canteenId, Long stallId) {
      return CharSequenceUtil.concat(true, new CharSequence[]{MarketDeviceTreeEnum.MOBILE.getDesc(), "&", NumberUtil.toStr(canteenId, ""), "&", NumberUtil.toStr(stallId, "")});
   }

   public static String formatBuffet(Long canteenId, Long stallId) {
      return Objects.isNull(canteenId) && Objects.isNull(stallId) ? MarketDeviceTreeEnum.BUFFET_CODE.getDesc() : CharSequenceUtil.concat(true, new CharSequence[]{MarketDeviceTreeEnum.BUFFET.getDesc(), "&", NumberUtil.toStr(canteenId, ""), "&", NumberUtil.toStr(stallId, "")});
   }

   public static String formatPayDeviceSn(Integer orderType, Long canteenId, String deviceSn) {
      OrderTypeEnum orderTypeEnum = OrderTypeEnum.getEnumByKey(orderType);
      if (Objects.isNull(orderTypeEnum)) {
         return deviceSn;
      } else {
         String finalDeviceSn;
         switch (orderTypeEnum) {
            case CURR_MEAL:
            case RESERVE_MEAL:
            case BOOK_MEAL:
            case BOOK_MEAL_3DAY:
            case BOOK_MEAL_7DAY:
               finalDeviceSn = CharSequenceUtil.concat(true, new CharSequence[]{MarketDeviceTreeEnum.MOBILE.getDesc(), "&", NumberUtil.toStr(canteenId, ""), "&"});
               break;
            case MOBILE_SCAN:
            case MOBILE_SCAN_SHOP:
            default:
               finalDeviceSn = deviceSn;
               break;
            case SHOP:
            case RESERVE_SHOP:
               finalDeviceSn = canteenId.toString();
               break;
            case DEDUCT:
               finalDeviceSn = CharSequenceUtil.isBlank(deviceSn) ? CharSequenceUtil.concat(true, new CharSequence[]{MarketDeviceTreeEnum.MOBILE.getDesc(), "&", NumberUtil.toStr(canteenId, ""), "&"}) : deviceSn;
         }

         return finalDeviceSn;
      }
   }
}
