package com.liquidnet.service.merchant.service.impl;

import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketEntitiesVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinPerformanceVo;
import com.liquidnet.service.merchant.constant.MerchantAuthorizationConst;
import com.liquidnet.service.merchant.dto.param.MerchantStationUploadParam;
import com.liquidnet.service.merchant.dto.vo.*;
import com.liquidnet.service.merchant.util.ObjectUtil;
import com.liquidnet.service.merchant.util.QueueUtil;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.WriteModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MerchantStationService {
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    QueueUtil queueUtil;

    /**
     * 查取用户存在权限的演出
     *
     * @param checkUserId UID
     * @return List<MerchantAuthorizationPerformanceVo>
     */
    public List<MerchantAuthorizationPerformanceVo> getCheckUserRelationVo(String checkUserId) {
        return mongoTemplate.find(
                Query.query(Criteria.where("uid").is(checkUserId).and("permissionIds").in(
                        MerchantAuthorizationConst.PerformancePermission.SALES.getId(),
                        MerchantAuthorizationConst.PerformancePermission.CHECK.getId())
                ),
                MerchantAuthorizationPerformanceVo.class, MerchantAuthorizationPerformanceVo.class.getSimpleName());
    }

    /**
     * 销售统计数据整理
     *
     * @param r                               演出基础信息
     * @param performanceTicketEntitiesVoList 演出订单票明细：Map<performanceId, List<KylinOrderTicketEntitiesVo>>
     */
    public void salesStatisticsSetter(MerchantStationPerformanceVo r, List<KylinOrderTicketEntitiesVo> performanceTicketEntitiesVoList) {
        try {
            List<KylinTicketVo> performanceTicketVoList = ObjectUtil.getKylinTicketVoArrayList();
            r.getTicketTimeList().forEach(tt -> performanceTicketVoList.addAll(tt.getTicketList()));// 提取演出对应票种信息

            // 转换票种信息结构为Map<ticketsId, ticketVo>
            Map<String, KylinTicketVo> performanceTicketMap = performanceTicketVoList.stream().collect(Collectors.toMap(KylinTicketVo::getTicketsId, Function.identity(), (k1, k2) -> k2));
            if (log.isDebugEnabled()) {
                log.debug("performanceTicketMap:{}", JsonUtils.toJson(performanceTicketMap));
            }

            // 订单票明细按票种分组
            Map<String, List<KylinOrderTicketEntitiesVo>> performanceTicketEntitiesVoMap =
                    performanceTicketEntitiesVoList.stream().collect(Collectors.groupingBy(KylinOrderTicketEntitiesVo::getTicketId));

            List<MerchantStationTicketVo> ticketVoList = ObjectUtil.getMerchantStationTicketVoArrayList();

            BigDecimal priceSum = BigDecimal.ZERO;
            int number = 0, checkedNum = 0, remainderNum = 0;
            for (Map.Entry<String, List<KylinOrderTicketEntitiesVo>> entry : performanceTicketEntitiesVoMap.entrySet()) {
                KylinTicketVo ticketVo = performanceTicketMap.get(entry.getKey());

                MerchantStationTicketVo stationTicketVo = MerchantStationTicketVo.getNew();
                stationTicketVo.setTicketId(ticketVo.getTicketsId());
                stationTicketVo.setTitle(ticketVo.getTitle());
                stationTicketVo.setPrice(ticketVo.getPrice());
                stationTicketVo.setUseStart(ticketVo.getUseStart());
                stationTicketVo.setUseEnd(ticketVo.getUseEnd());

                List<KylinOrderTicketEntitiesVo> subPerformanceTicketEntitiesVoList = entry.getValue();
                // 订单票明细按出票状态分组
                Map<Integer, List<KylinOrderTicketEntitiesVo>> subStatusPerformanceTicketEntitiesVoMap
                        = subPerformanceTicketEntitiesVoList.stream().collect(Collectors.groupingBy(KylinOrderTicketEntitiesVo::getStatus));

                stationTicketVo.setNumber(subPerformanceTicketEntitiesVoList.size());
                stationTicketVo.setPriceSum(stationTicketVo.getPrice().multiply(BigDecimal.valueOf(stationTicketVo.getNumber())));
                List<KylinOrderTicketEntitiesVo> checkedEntitiesVoList = subStatusPerformanceTicketEntitiesVoMap.get(1);
                stationTicketVo.setCheckedNum(CollectionUtils.isEmpty(checkedEntitiesVoList) ? 0 : checkedEntitiesVoList.size());
                List<KylinOrderTicketEntitiesVo> remainderEntitiesVoList = subStatusPerformanceTicketEntitiesVoMap.get(0);
                stationTicketVo.setRemainderNum(CollectionUtils.isEmpty(remainderEntitiesVoList) ? 0 : remainderEntitiesVoList.size());

                number += stationTicketVo.getNumber();
                checkedNum += stationTicketVo.getCheckedNum();
                remainderNum += stationTicketVo.getRemainderNum();
                priceSum = priceSum.add(stationTicketVo.getPriceSum());

                ticketVoList.add(stationTicketVo);
            }
            r.setPriceSum(priceSum);
            r.setNumber(number);
            r.setCheckedNum(checkedNum);
            r.setRemainderNum(remainderNum);
            r.setTicketVoList(ticketVoList);
        } catch (Exception e) {
            log.error("验票:整合销售统计异常[station/performances],performancesId:{}", r.getPerformancesId(), e);
        }
    }

    /**
     * 上载验票数据
     *
     * @param parameter MerchantStationUploadParam
     */
    public void updateByStation(MerchantStationUploadParam parameter) {
//        // 根据验票用户ID分组
//        Map<String, List<KylinStationCheckOrderParam>> checkOrderByUserIdMap = checkOrderParamList.stream().collect(Collectors.groupingBy(KylinStationCheckOrderParam::getCheckUserId));
//
//        for (Map.Entry<String, List<KylinStationCheckOrderParam>> entry : checkOrderByUserIdMap.entrySet()) {
//            List<KylinStationCheckOrderParam> checkOrderByUserList = entry.getValue();
//
//            // 根据验票类型分组
//            Map<String, List<KylinStationCheckOrderParam>> checkOrderByCheckTypeMap = checkOrderByUserList.stream().collect(Collectors.groupingBy(KylinStationCheckOrderParam::getCheckType));
//        }

        LocalDateTime nowDt = LocalDateTime.now();
        String nowDtStr = DateUtil.format(nowDt, DateUtil.Formatter.yyyyMMddHHmmss);
        LinkedList<Object[]> paramsList = CollectionUtil.linkedListObjectArr();
        List<WriteModel<Document>> list = ObjectUtil.getWriteModelDocumentArrayList();
        parameter.getCheckOrderParamList().forEach(r -> {
            if (StringUtils.isNotBlank(r.getTicketEntitiesId())) {
                KylinOrderTicketEntitiesVo updateVo = KylinOrderTicketEntitiesVo.getNew();
                updateVo.setStatus(1);// 出票状态: 0未出票 1已出票
                updateVo.setCheckClient(parameter.getCheckClient());
                updateVo.setUpdatedAt(nowDtStr);
                updateVo.setCheckType(r.getCheckType());
                updateVo.setCheckedAt(r.getCheckedAt());
                updateVo.setCheckUserId(r.getCheckUserId());

                paramsList.add(new Object[]{1, updateVo.getCheckClient() + updateVo.getCheckType(), nowDt, r.getTicketEntitiesId()});
                Document updateQuery = Query.query(Criteria.where("orderTicketEntitiesId").is(r.getTicketEntitiesId())).getQueryObject();
                Document updateDoc = new Document("status", updateVo.getStatus())
                        .append("checkClient", updateVo.getCheckClient())
                        .append("updatedAt", updateVo.getUpdatedAt())
                        .append("checkType", updateVo.getCheckType())
                        .append("checkedAt", updateVo.getCheckedAt())
                        .append("checkUserId", updateVo.getUserId());
                list.add(new UpdateOneModel<>(updateQuery, new Document("$set", updateDoc)));
            }
        });
        BulkWriteResult bulkWriteResult = mongoTemplate.getCollection(KylinOrderTicketEntitiesVo.class.getSimpleName()).bulkWrite(list);
        log.info("验票:上载验票数据[bulkWriteResult:{}]", JsonUtils.toJson(bulkWriteResult));
        if (bulkWriteResult.getModifiedCount() > 0) {
            queueUtil.sendMsgByRedis(MQConst.KylinQueue.SQL_STATION.getKey(),
                    SqlMapping.get("kylin_order_ticket_entities.update_status_bystation", paramsList));
        }
    }

    /**
     * 下载、更新验票数据
     *
     * @param authorizationPerformanceVo 用户演出权限
     * @param performanceId              演出ID
     * @param latestUpdateAt             最近更新时间（仅当更新时必传，下载时传null）
     * @return MerchantStationCheckRefreshVo
     */
    public MerchantStationCheckRefreshVo downloadRefreshTicketData(MerchantAuthorizationPerformanceVo authorizationPerformanceVo, String performanceId, String latestUpdateAt) {
        Criteria criteria = Criteria.where("performanceId").is(performanceId).and("isPayment").is(1);
        if (StringUtils.isNotEmpty(latestUpdateAt)) {
            criteria.and("updatedAt").gte(latestUpdateAt);
        }
        // 查取演出对应的订单票明细
        Query orderTicketEntitiesVoQuery = Query.query(criteria);
        List<MerchantStationCheckOrderVo> checkOrderVos = mongoTemplate.find(orderTicketEntitiesVoQuery, MerchantStationCheckOrderVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());
        // 查取订单信息(orderTicketsId,userMobile,userName,qrCode)
        Query query = Query.query(Criteria.where("orderTicketsId").in(checkOrderVos.stream().map(MerchantStationCheckOrderVo::getOrderId).toArray()));
        query.fields().include("orderTicketsId").include("userMobile").include("userName").include("qrCode");
        List<KylinOrderTicketVo> orderTicketVoList = mongoTemplate.find(query, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
        for (KylinOrderTicketVo t : orderTicketVoList) {
            for (MerchantStationCheckOrderVo r : checkOrderVos) {
                if (r.getOrderId().equals(t.getOrderTicketsId())) {
                    r.setQrCode(t.getQrCode());
                    r.setUserMobile(t.getUserMobile());
                    r.setUserName(t.getUserName());
                }
            }
        }

        // 查取演出信息
        MerchantStationPerformanceVo performanceVo = mongoTemplate.findOne(Query.query(Criteria.where("performancesId").is(performanceId)),
                MerchantStationPerformanceVo.class, KylinPerformanceVo.class.getSimpleName());
        // 提取演出对应票种信息
        List<KylinTicketVo> performanceTicketVoList = ObjectUtil.getKylinTicketVoArrayList();
        performanceVo.getTicketTimeList().forEach(tt -> performanceTicketVoList.addAll(tt.getTicketList()));

        List<MerchantStationTicketVo> ticketVoList = ObjectUtil.getMerchantStationTicketVoArrayList();
        for (KylinTicketVo r : performanceTicketVoList) {
            MerchantStationTicketVo stationTicketVo = MerchantStationTicketVo.getNew();
            stationTicketVo.setTicketId(r.getTicketsId());
            stationTicketVo.setTitle(r.getTitle());
            stationTicketVo.setPrice(r.getPrice());
            stationTicketVo.setUseStart(r.getUseStart());
            stationTicketVo.setUseEnd(r.getUseEnd());

            ticketVoList.add(stationTicketVo);
        }
        performanceVo.setPermissionIds(StringUtils.join(authorizationPerformanceVo.getPermissionIds().toArray(), "."));
        performanceVo.setTicketTimeList(null);
        performanceVo.setTicketVoList(ticketVoList);

        MerchantStationCheckRefreshVo stationCheckRefreshVo = MerchantStationCheckRefreshVo.getNew();
        stationCheckRefreshVo.setCheckOrderVos(checkOrderVos);
        stationCheckRefreshVo.setPerformanceVo(performanceVo);
        return stationCheckRefreshVo;
    }
}
