package com.zm.order.tool.server.handler;

import com.alibaba.fastjson.JSON;
import com.github.dozermapper.core.Mapper;
import com.google.common.collect.Lists;
import com.zm.order.tool.server.builder.SearchRequestBuilder;
import com.zm.order.tool.server.constants.CommonConstants;
import com.zm.order.tool.server.dao.OrderItemDao;
import com.zm.order.tool.server.dao.OrderPayDetailsDao;
import com.zm.order.tool.server.dao.RefundDao;
import com.zm.order.tool.server.dto.OrderDTO;
import com.zm.order.tool.server.dto.RefundDTO;
import com.zm.order.tool.server.enums.BizTypeEnum;
import com.zm.order.tool.server.helper.OrderWriteServiceHelper;
import com.zm.order.tool.server.model.*;
import com.zm.order.tool.server.query.SearchOrderQuery;
import com.zm.order.tool.server.utils.OrderCommonUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * BU线正餐服务
 *
 * <p>
 *      异步构建ES检索文档与同步构建ES检索语句处理器
 * </p>
 *
 * @author nianchang
 * @date 2023/02/15 1:52 下午
 */
@Component
public class MealMateBusinessHandler implements BusinessHandler {

    private static final Logger logger = LoggerFactory.getLogger(MealMateBusinessHandler.class);

    @Autowired
    private RefundDao refundDao;

    @Autowired
    private Mapper mapper;

    @Autowired
    private OrderWriteServiceHelper helper;
    @Autowired
    private OrderPayDetailsDao payDetailsDao;
    @Autowired
    private OrderItemDao orderItemDao;
    /**
     * 支付已状态
     */
    private static final Integer ORDER_PAYED = 1;

    /**
     * 退款完成
     */
    private static final Integer REFUND_COMPLETED = 30;


    /**
     * 配送状态
     */
    private static final String DELIVERY_STATUS = "deliveryStatus";

    /**
     * 收货手机号
     */
    String RECEIVER_MOBILE = "receiverMobile";

    /**
     * 预约时间
     */
    String RESERVE_TIME = "reserveTime";

    /**
     * 支付状态
     */
    private static final Integer ORDER_PAY_STATUS = 1;

    /**
     * 桌位名称
     */
    private static final String SEAT_NAME = "seatName";

    /**
     * 同步ERP异常
     */
    private static final String SYNC_ERROR = "syncError";

    /**
     * 渠道码
     */
    private static final String CHANNEL_CODE = "channelCode";

    /**
     * 账单号
     */
    private static final String BILL_NO = "billNo";

    /**
     * 拼团状态
     */
    private static final String GROUP_STATUS = "groupStatus";

    private static final String USE_WALLET = "useWallet";
    /**
     * 入账类型
     */
    String RECEIVE_PAYMENT_TYPE = "receivePaymentType";

    /**
     * 餐牌号
     */
    String MEAL_TAKING_DEVICE_NO = "mealTakingDeviceNo";

    /**
     * 商品订单类型
     */
    String GOODS_TYPE = "goodsSubType";

    /**
     * 雷达订单前缀
     */
    String RADAR_ORDER_SCRM = "scrm";
    /**
     * 实体储值卡类型
     */
    String DEPOSIT_ENTITY_BIZ_TYPE = "depositEntityBizType";

    /**
     * 推广码id
     */
    String ORDER_PROMOTE_CODE = "orderPromoteCode";

    /**
     * 拼团类型
     */
    private static final String GROUP_TYPE = "groupType";

    /**
     * 订单排队状态
     */
    String ORDER_LINE_UP_STATUS = "lineUpStatus";

    /**
     * 进排队时间
     */
    String QUEUE_TIME = "queueTime";

    /**
     * 企业订单信息
     */
    String EXTRA_ENTERPRISE_ORDER_INFO = "extraEnterpriseOrderInfo";

    /**
     * 企业订单id
     */
    String ENTERPRISE_ID = "enterpriseId";


    @Override
    public Integer bizType() { return BizTypeEnum.MEAL_MATE.getType(); }

    @Override
    public BaseIndexOrder buildIndexOrder(Order order) {
        MealMateIndexOrder index = mapper.map(order, MealMateIndexOrder.class);
        buildBaseInfo(index, order);
        buildItemInfo(index, order);
        buildExtInfo(index, order);
        buildExtInfo1(index, order);
        buildPerInfo(index, order);
        buildPayInfo(index, order);
        buildLikeSearch(index, order);
        buildAddressInfo(index, order);
        return index;
    }

    private void buildBaseInfo(MealMateIndexOrder indexOrder, Order order) {
        indexOrder.setRefundCompleted(0);
        indexOrder.setOrderId(order.getId());
        indexOrder.setSearchPayer(order.getPayer());
        if (order.getCompletedAt() != null) {
            indexOrder.setSearchCompletedAt(order.getCompletedAt());
        }
        indexOrder.setRefundCompleted(0);
        if (Objects.equals(1, order.getPayStatus())) {
            try {
                indexOrder.setRefundCompleted(order.getRefundFlag());
            } catch (Exception e) {
                logger.error("[SELECT_REFUND_ID_LIST]failed! cause by:{}", e);
            }
        }
        final Integer actualAmount = order.getActualAmount();
        indexOrder.setSearchActualAmount(OrderCommonUtils.decideIfNotNull(actualAmount, actualAmount));
    }

    private void buildAddressInfo(MealMateIndexOrder indexOrder, Order order) {
        try {
            if (!StringUtils.isEmpty(order.getAddrInfo())) {
                InnerOrderAddr orderAddr = JSON.parseObject(order.getAddrInfo(), InnerOrderAddr.class);
                indexOrder.setAcceptMobile(orderAddr.getMobile());
                indexOrder.setAcceptName(orderAddr.getAcceptName());
            }
        } catch (Exception e) {
            logger.error("MealMateBusinessHandler[buildAddressInfo]failed! orderNo:{}, cause by:{}", order.getOrderNo(), e);
        }
    }

    private void buildPayInfo(MealMateIndexOrder indexOrder, Order order) {
        try {
            // 记录三方支付金额
            indexOrder.setAmount(Objects.nonNull(order.getPayAmount()) ? order.getPayAmount() : 0);
            setWalletAmount(indexOrder, order);
            setPayTypeList(indexOrder, order);
        } catch (Exception e) {
            logger.error("buildPayInfo[BUILD_PAY_AMOUNT_ERROR]failed! order:{}, cause by:{}", order, e);
        }
    }

    private void setWalletAmount(MealMateIndexOrder indexOrder, Order order) {
        String payInfo = order.getPayInfo();
        if (StringUtils.isEmpty(payInfo)) {
            return;
        }
        Map<String, Object> payInfoMap = JSON.parseObject(payInfo);
        if (Objects.isNull(payInfoMap.get(USE_WALLET))) {
            return;
        }
        Integer useWallet = Integer.valueOf(payInfoMap.get(USE_WALLET).toString());
        // 支付方式中存在余额支付
        Integer walletAmount = 0;
        if (Objects.equals(1, useWallet)) {
            List<OrderPayDetails> payDetailsList = this.parsePayList(order);
            if (!CollectionUtils.isEmpty(payDetailsList)) {
                for (OrderPayDetails payDetails : payDetailsList) {
                    // payType = 4 余额支付
                    if (Objects.equals(payDetails.getPayType(), 4)) {
                        walletAmount = payDetails.getAmount();
                        break;
                    }
                }
            }
        }
        indexOrder.setSearchWalletAmount(walletAmount);
    }

    private void setPayTypeList(MealMateIndexOrder indexOrder, Order order) {
        List<OrderPayDetails> payDetailsList = this.parsePayList(order);
        List<Integer> payTypeList = new ArrayList<>(payDetailsList.size());
        if (!CollectionUtils.isEmpty(payDetailsList)) {
            for (OrderPayDetails payDetails : payDetailsList) {
                payTypeList.add(payDetails.getPayType());
            }
        }
        indexOrder.setPayTypeList(payTypeList);
    }

    private List<OrderPayDetails> parsePayList(Order order) {
        List<OrderPayDetails> payDetailsList;
        String payDetailInfo = order.getPayDetailInfo();
        if (!StringUtils.isEmpty(payDetailInfo) && !Objects.equals(payDetailInfo, CommonConstants.PAY_FLAG)) {
            payDetailsList = JSON.parseArray(payDetailInfo, OrderPayDetails.class);
        } else {
            payDetailsList = payDetailsDao.selectByOrderNo(order.getOrderNo(), order.getUserId());
        }
        return payDetailsList;
    }

    private void buildPerInfo(MealMateIndexOrder indexOrder, Order order) {
        if (!StringUtils.isEmpty(order.getPerformanceInfo())) {
            Map<String, Object> performanceMap = JSON.parseObject(order.getPerformanceInfo());
            if (!CollectionUtils.isEmpty(performanceMap)) {
                if (Objects.nonNull(performanceMap.get(DELIVERY_STATUS))) {
                    indexOrder.setDeliveryStatus(Integer.valueOf(performanceMap.get(DELIVERY_STATUS).toString()));
                }
                if (!StringUtils.isEmpty(performanceMap.get(RECEIVER_MOBILE))) {
                    String receiverMobile = performanceMap.get(RECEIVER_MOBILE).toString();
                    indexOrder.setReceiverMobile(receiverMobile);
                }
                if (!StringUtils.isEmpty(performanceMap.get(RESERVE_TIME))) {
                    String reserveTime = performanceMap.get(RESERVE_TIME).toString();
                    try {
                        Date date = new Date();
                        date.setTime(Long.parseLong(reserveTime));
                        indexOrder.setReserveTime(date);
                    } catch (Exception ex) {
                        logger.error("MealMateBusinessHandler[buildPerInfoError] time:{} orderNo:{}, cause by:{}",
                                reserveTime, order.getOrderNo(), ex);
                    }
                }
            }
        }
    }

    private void buildExtInfo(MealMateIndexOrder indexOrder, Order order) {
        Map<String, Object> extraMap = order.getExtraMap();
        if (!CollectionUtils.isEmpty(extraMap)) {
            if (!StringUtils.isEmpty(extraMap.get(SEAT_NAME))) {
                String seatName = extraMap.get(SEAT_NAME).toString();
                indexOrder.setSeatName(seatName);
                String globalSearch = indexOrder.getGlobalSearch();
                if (StringUtils.isEmpty(globalSearch)) {
                    globalSearch = seatName;
                } else {
                    globalSearch = globalSearch + "," + seatName;
                }
                indexOrder.setGlobalSearch(globalSearch);
            }
            if (Objects.nonNull(extraMap.get(SYNC_ERROR))) {
                indexOrder.setSyncErpError(Integer.valueOf(extraMap.get(SYNC_ERROR).toString()));
            }
            if (!StringUtils.isEmpty(extraMap.get(GROUP_STATUS))) {
                indexOrder.setGroupStatus(extraMap.get(GROUP_STATUS).toString());
            }
            if (Objects.nonNull(extraMap.get(GROUP_TYPE))) {
                indexOrder.setGroupType(Integer.valueOf(extraMap.get(GROUP_TYPE).toString()));
            }
            if (!StringUtils.isEmpty(extraMap.get(CHANNEL_CODE))) {
                indexOrder.setChannelCode(extraMap.get(CHANNEL_CODE).toString());
            }
            if (Objects.nonNull(extraMap.get(RECEIVE_PAYMENT_TYPE))) {
                indexOrder.setReceivePaymentType(Integer.valueOf(extraMap.get(RECEIVE_PAYMENT_TYPE).toString()));
            }
            if (Objects.nonNull(extraMap.get(GOODS_TYPE))) {
                indexOrder.setGoodsSubType(Integer.valueOf(extraMap.get(GOODS_TYPE).toString()));
            }
            if (!StringUtils.isEmpty(extraMap.get(BILL_NO))) {
                indexOrder.setBillNo(extraMap.get(BILL_NO).toString());
            }
            if (!StringUtils.isEmpty(extraMap.get(MEAL_TAKING_DEVICE_NO))) {
                String globalSearch = indexOrder.getGlobalSearch();
                indexOrder.setGlobalSearch(globalSearch + "," + extraMap.get(MEAL_TAKING_DEVICE_NO));
            }
            if (extraMap.get(ORDER_LINE_UP_STATUS) != null) {
                indexOrder.setLineUpStatus(Integer.valueOf(extraMap.get(ORDER_LINE_UP_STATUS).toString()));
            }
            if (extraMap.get(QUEUE_TIME) != null) {
                indexOrder.setSearchQueueTime(new Date(Long.parseLong(extraMap.get(QUEUE_TIME).toString())));
            }
            if (Objects.nonNull(extraMap.get(EXTRA_ENTERPRISE_ORDER_INFO))) {
                Map<String, Object> infoMap = JSON.parseObject(extraMap.get(EXTRA_ENTERPRISE_ORDER_INFO).toString());
                if (Objects.nonNull(infoMap.get(ENTERPRISE_ID))) {
                    indexOrder.setEnterpriseId(infoMap.get(ENTERPRISE_ID).toString());
                }
            }
            if (!StringUtils.isEmpty(extraMap.get(CHANNEL_CODE))) {
                String channelCode = extraMap.get(CHANNEL_CODE).toString();
                if (channelCode.startsWith(RADAR_ORDER_SCRM)) {
                    indexOrder.setRadarOrder(1);
                }
            }
            Object depositEntityBizType = extraMap.get(DEPOSIT_ENTITY_BIZ_TYPE);
            if (Objects.nonNull(depositEntityBizType)) {
                indexOrder.setDepositEntityBizType(Integer.valueOf(depositEntityBizType.toString()));
            }
        }
    }

    private void buildExtInfo1(MealMateIndexOrder indexOrder, Order order){
        Map<String, Object> extraMap = order.getExtraMap();
        if (!CollectionUtils.isEmpty(extraMap)) {
            if (!StringUtils.isEmpty(extraMap.get(ORDER_PROMOTE_CODE))) {
                indexOrder.setOrderPromoteCode(extraMap.get(ORDER_PROMOTE_CODE).toString());
            }
            if (!StringUtils.isEmpty(extraMap.get(ORDER_PROMOTE_CODE))) {
                indexOrder.setOrderPromoteCode(extraMap.get(ORDER_PROMOTE_CODE).toString());
            }
        }
    }

    private void buildItemInfo(MealMateIndexOrder indexOrder, Order order) {
        List<OrderItem> itemList = this.getItemList(order);
        List<String> searchCondition = new ArrayList<>();
        this.parseItemAttribute(indexOrder, itemList, searchCondition);
        this.setSearchAttribute(indexOrder, order, searchCondition);
    }

    private List<OrderItem> getItemList(Order order) {
        List<OrderItem> itemList;
        if (StringUtils.isEmpty(order.getItemInfo()) || Objects.equals(CommonConstants.ITEM_FLAG, order.getItemInfo())) {
            itemList = orderItemDao.selectByOrderNo(order.getOrderNo(), order.getUserId());
        } else {
            itemList = JSON.parseArray(order.getItemInfo(), OrderItem.class);
        }
        return itemList;
    }
    /**
     * 解析商品属性赋值
     *
     * <p>
     *      获取商品type支持检索
     *      获取商品名称、itemId支持检索
     * </p>
     *
     * @param index	ES mapping文档
     * @param itemList 商品列表
     * @param searchCondition 全局搜索条件
     */
    private void parseItemAttribute(MealMateIndexOrder index, List<OrderItem> itemList, List<String> searchCondition) {
        if (CollectionUtils.isEmpty(itemList)) {
            return;
        }
        final int size = itemList.size();
        Set<Integer> itemTypeSet = new HashSet<>(size);
        Set<String> itemIdList = new HashSet<>(size);
        Set<String> itemNameList = new HashSet<>(size);
        Set<String> itemBarcodeList = new HashSet<>(size);
        helper.parseItem(itemList, searchCondition, itemTypeSet, itemIdList, itemNameList, itemBarcodeList);

        index.setItemIdList(Lists.newArrayList(itemIdList));
        index.setItemBarcodeList(Lists.newArrayList(itemBarcodeList));
        index.setItemTypeList(Lists.newArrayList(itemTypeSet));
        index.setItemNamesKeyword(Lists.newArrayList(itemNameList));
    }

    /**
     * 全局搜索
     *
     * <p>
     *      多个条件拼接在一起，用','分割，多条件精准搜索
     *      现有条件：订单号、手机号、取餐号、三方单号、商品名称、商品id、桌台号
     *      后续可能还会增加
     * </p>
     *
     * @param indexOrder ES mapping文档
     * @param order 订单详情
     * @param searchCondition 全局搜索条件
     */
    private void setSearchAttribute(MealMateIndexOrder indexOrder, Order order, List<String> searchCondition) {
        searchCondition.add(order.getMobile());
        searchCondition.add(order.getOrderNo());
        if (!StringUtils.isEmpty(order.getSourceNo())) {
            searchCondition.add(order.getSourceNo());
        }
        if (!StringUtils.isEmpty(order.getStoreOrderNo())) {
            searchCondition.add(order.getStoreOrderNo());
        }
        indexOrder.setGlobalSearch(OrderCommonUtils.join(",", searchCondition));
    }

    private void buildLikeSearch(MealMateIndexOrder index, Order order) {
        index.setLikeSearch(helper.addElement(order.getOrderNo(), order.getMobile()));
    }

    @Override
    public SearchSourceBuilder buildSearchSourceBuilder(SearchOrderQuery searchOrderQuery) {
        SearchSourceBuilder searchSourceBuilder = SearchRequestBuilder.buildOrderSearchBuilder(searchOrderQuery);
        BoolQueryBuilder boolQueryBuilder = (BoolQueryBuilder) searchSourceBuilder.query();
        return searchSourceBuilder;
    }

    @Override
    public List<BaseIndexOrder> parseIndexOrder(SearchHit[] searchHits) {
        List<BaseIndexOrder> list = Lists.newArrayListWithCapacity(searchHits.length);
        for (SearchHit searchHit : searchHits) {
            MealMateIndexOrder t = JSON.parseObject(searchHit.getSourceAsString(), MealMateIndexOrder.class);
            list.add(t);
        }
        return list;
    }

    @Override
    public Integer getRefundAmount(RefundDTO refundDTO) {
        return Objects.equals(refundDTO.getStatus(), REFUND_COMPLETED) ? refundDTO.getRefundAmount() : 0;
    }

    @Override
    public BaseIndexOrder getOrderDto(BaseIndexOrder baseIndexOrder, OrderDTO orderDTO, Order order) {
        return null;
    }
}
