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

import com.fasterxml.jackson.databind.JsonNode;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.merchant.dto.param.MerchantFieldApplyParam;
import com.liquidnet.service.merchant.dto.vo.MerchantFieldAppliesVo;
import com.liquidnet.service.merchant.dto.vo.MerchantFieldsVo;
import com.liquidnet.service.merchant.service.IMerchantFieldAppliesService;
import com.liquidnet.service.merchant.service.MerchantMongoService;
import com.liquidnet.service.merchant.service.MerchantRdmService;
import com.liquidnet.service.merchant.util.QueueUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
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.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.IntStream;

@Slf4j
@Service
public class MerchantFieldAppliesServiceImpl implements IMerchantFieldAppliesService {

    @Autowired
    MerchantRdmService merchantRdmService;

    @Autowired
    MerchantMongoService merchantMongoService;

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    QueueUtil queueUtil;

    @Override
    public String apply(String uid, MerchantFieldApplyParam parameter) {
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldUpdateObjs = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> fieldApplyInsertObjs = CollectionUtil.linkedListObjectArr();

        // 若认领，查询验证场地
        MerchantFieldsVo fieldsVo = null;
        if (null != parameter.getFieldId() && !parameter.getFieldId().isEmpty()) {
            fieldsVo = merchantRdmService.getFieldsVoByFieldId(parameter.getFieldId());
            if (null == fieldsVo) {
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13101");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            }
            if (0 != fieldsVo.getClaimStatus()) {
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13102");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            }
        }

        // 申请场地 vos 上限
        List<MerchantFieldAppliesVo> fieldAppliesVos = merchantRdmService.getFieldAppliesVosByUid(uid);
        if (!CollectionUtils.isEmpty(fieldAppliesVos)) {
            if (fieldAppliesVos.size() >= 10) {
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13111");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            }
        }

        LocalDateTime now = LocalDateTime.now();
        long s = System.currentTimeMillis();
        if (null != fieldsVo) {
            // 场地 vo 更改场地状态 及 认领的人
            fieldsVo.setClaimStatus(2);
            fieldsVo.setUid(uid);
            fieldsVo.setUpdatedAt(now);

            // 场地 redis
            merchantRdmService.setFieldsVoByFieldId(fieldsVo.getFieldId(), fieldsVo);
            log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

            // 场地 mongo
            s = System.currentTimeMillis();
            Query query = Query.query(Criteria.where("fieldId").is(fieldsVo.getFieldId()));
            Update update = Update.update("claimStatus", fieldsVo.getClaimStatus()).set("uid", fieldsVo.getUid()).set("updatedAt", fieldsVo.getUpdatedAt());
            mongoTemplate.updateFirst(query, update, MerchantFieldsVo.class, MerchantFieldsVo.class.getSimpleName());
            log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);
        }

        // 申请场地 vo
        MerchantFieldAppliesVo fieldAppliesVo = MerchantFieldAppliesVo.getNew();
        BeanUtils.copyProperties(parameter, fieldAppliesVo);
        fieldAppliesVo.setFieldApplyId(IDGenerator.nextSnowId());
        fieldAppliesVo.setApplyStatus(0);
        fieldAppliesVo.setReject("");
        fieldAppliesVo.setUid(uid);
        fieldAppliesVo.setCreatedAt(now);
        if (null != fieldsVo) {
            fieldAppliesVo.setApplyType("claim");
            fieldAppliesVo.setFieldId(fieldsVo.getFieldId());
        } else {
            fieldAppliesVo.setApplyType("create");
            fieldAppliesVo.setFieldId("");
        }
        LocalDate builtDate = LocalDate.parse(parameter.getBuiltDate(), DateTimeFormatter.ofPattern(DateUtil.DATE_SMALL_STR));
        fieldAppliesVo.setBuiltDate(builtDate);

        // 身份证检查
        String respStr = IdentityUtils.aliThird(fieldAppliesVo.getLegalName(), fieldAppliesVo.getLegalIdentity());;
        JsonNode respJNode = JsonUtils.fromJson(respStr, JsonNode.class);
        if (null == respJNode || !"0".equals(respJNode.get("error_code").asText())) {
            log.info("###实名认证失败[{}]", respStr);
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13001");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        // 申请场地 redis
        s = System.currentTimeMillis();
        merchantRdmService.addFieldAppliesVoByUid(uid, fieldAppliesVos, fieldAppliesVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        // 申请场地 mongo
        s = System.currentTimeMillis();
        merchantMongoService.addFieldAppliesVo(fieldAppliesVo);
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        if (null != fieldsVo) {
            // 场地 sql
            toMqSqls.add(SqlMapping.get("merchant_fields.update_claim_status_and_uid"));
            fieldUpdateObjs.add(new Object[]{
                    fieldsVo.getClaimStatus(), fieldsVo.getUid(), fieldsVo.getUpdatedAt(), fieldsVo.getFieldId()
            });
        }
        // 申请场地 sql
        toMqSqls.add(SqlMapping.get("merchant_field_applies.insert"));
        fieldApplyInsertObjs.add(new Object[]{
                fieldAppliesVo.getFieldApplyId(), fieldAppliesVo.getApplyStatus(), fieldAppliesVo.getApplyType(), fieldAppliesVo.getReject(), fieldAppliesVo.getUid(), fieldAppliesVo.getFieldId(),
                fieldAppliesVo.getName(), fieldAppliesVo.getLogo(), fieldAppliesVo.getBackground(), fieldAppliesVo.getDescription(), fieldAppliesVo.getBuiltDate(),
                fieldAppliesVo.getProvinceId(), fieldAppliesVo.getProvinceName(), fieldAppliesVo.getCityId(), fieldAppliesVo.getCityName(), fieldAppliesVo.getDistrictId(), fieldAppliesVo.getDistrictName(), fieldAppliesVo.getAddress(), fieldAppliesVo.getLongitude(), fieldAppliesVo.getLatitude(),
                fieldAppliesVo.getContactName(), fieldAppliesVo.getContactEmail(),
                fieldAppliesVo.getCompanyName(), fieldAppliesVo.getLicenseCode(), fieldAppliesVo.getLicenseImg(),
                fieldAppliesVo.getLegalName(), fieldAppliesVo.getLegalIdentity(), fieldAppliesVo.getLegalIdentityObverse(), fieldAppliesVo.getLegalIdentityReverse(), fieldAppliesVo.getCreatedAt()
        });

        // mq
        s = System.currentTimeMillis();
        if (null != fieldsVo) {
            queueUtil.sendMsgByRedis(
                    MQConst.MerchantQueue.SQL_MERCHANT_FIELD.getKey(),
                    SqlMapping.gets(toMqSqls, fieldUpdateObjs, fieldApplyInsertObjs)
            );
        } else {
            queueUtil.sendMsgByRedis(
                    MQConst.MerchantQueue.SQL_MERCHANT_FIELD.getKey(),
                    SqlMapping.gets(toMqSqls, fieldApplyInsertObjs)
            );
        }
        log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);

        return fieldAppliesVo.getFieldApplyId();
    }

    @Override
    public void applyDel(String uid, String fieldApplyId) {
        // 查找场地申请，是否存在且已经驳回
        List<MerchantFieldAppliesVo> fieldAppliesVos = merchantRdmService.getFieldAppliesVosByUid(uid);
        if (CollectionUtils.isEmpty(fieldAppliesVos)) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13112");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        int idx = IntStream.range(0, fieldAppliesVos.size())
                .filter(i -> fieldAppliesVos.get(i).getFieldApplyId().equals(fieldApplyId))
                .findFirst()
                .orElse(-1);
        if (idx < 0) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13112");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        MerchantFieldAppliesVo fieldAppliesVo = fieldAppliesVos.get(idx);
        if (fieldAppliesVo.getApplyStatus() != 2) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13113");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        LocalDateTime now = LocalDateTime.now();
        fieldAppliesVo.setUpdatedAt(now);
        fieldAppliesVo.setDeletedAt(now);

        // 申请场地 redis
        long s = System.currentTimeMillis();
        merchantRdmService.delFieldAppliesVoByUid(uid, fieldAppliesVos, fieldAppliesVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        // 申请场地 mongo
        s = System.currentTimeMillis();
        merchantMongoService.delFieldAppliesVoByFieldApplyId(fieldAppliesVo.getFieldApplyId());
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        // 申请场地 sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldAppliesUpdateObjs = CollectionUtil.linkedListObjectArr();
        toMqSqls.add(SqlMapping.get("merchant_field_applies.update"));
        fieldAppliesUpdateObjs.add(new Object[]{
                fieldAppliesVo.getUpdatedAt(), fieldAppliesVo.getDeletedAt(), fieldAppliesVo.getFieldApplyId()
        });

        // mq
        s = System.currentTimeMillis();
        queueUtil.sendMsgByRedis(
                MQConst.MerchantQueue.SQL_MERCHANT_FIELD.getKey(),
                SqlMapping.gets(toMqSqls, fieldAppliesUpdateObjs)
        );
        log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);
    }

    @Override
    public MerchantFieldAppliesVo applyInfo(String uid, String fieldApplyId) {
        // 查找场地申请，是否存在
        List<MerchantFieldAppliesVo> fieldAppliesVos = merchantRdmService.getFieldAppliesVosByUid(uid);
        if (CollectionUtils.isEmpty(fieldAppliesVos)) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13112");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        int idx = IntStream.range(0, fieldAppliesVos.size())
                .filter(i -> fieldAppliesVos.get(i).getFieldApplyId().equals(fieldApplyId))
                .findFirst()
                .orElse(-1);
        if (idx < 0) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13112");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        MerchantFieldAppliesVo fieldAppliesVo = fieldAppliesVos.get(idx);
        if (null != fieldAppliesVo.getFieldId() && !fieldAppliesVo.getFieldId().isEmpty()) {
            MerchantFieldsVo fieldsVo = merchantRdmService.getFieldsVoByFieldId(fieldAppliesVo.getFieldId());
            fieldAppliesVo.setFieldVo(fieldsVo);
        }

        return fieldAppliesVos.get(idx);
    }

}
