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.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.IdentityUtils;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.PagedResult;
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.MerchantFieldCheckersVo;
import com.liquidnet.service.merchant.dto.vo.MerchantFieldsVo;
import com.liquidnet.service.merchant.service.IMerchantFieldsService;
import com.liquidnet.service.merchant.service.MerchantRdmService;
import com.liquidnet.service.merchant.util.ObjectUtil;
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.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
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.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

@Slf4j
@Service
public class MerchantFieldsServiceImpl implements IMerchantFieldsService {

    @Autowired
    MerchantRdmService merchantRdmService;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    QueueUtil queueUtils;

    @Override
    public PagedResult<MerchantFieldsVo> search(String name, int page, int size) {
        // 查询条件
        Query query = new Query();
        String regex = String.format("%s%s%s", "^.*", name, ".*$");
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        query.addCriteria(Criteria.where("name").regex(pattern));
        query.addCriteria(Criteria.where("isOnline").is(1));

        // 总数
        long count = mongoTemplate.count(query, MerchantFieldsVo.class, MerchantFieldsVo.class.getSimpleName());

        // 分页
        PagedResult<MerchantFieldsVo> pagedResult = ObjectUtil.getMerchantFieldsVoPagedResult();
        if (count > 0) {
            query.fields().include("fieldId").include("isOnline").include("claimStatus").include("name").include("uid").include("isCheck");

            // 查询分页
            Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdAt"));
            query.with(pageable);
            List<MerchantFieldsVo> fieldsVoList = mongoTemplate.find(query, MerchantFieldsVo.class, MerchantFieldsVo.class.getSimpleName());

            pagedResult.setList(fieldsVoList).setTotal(count, size);
        }

        return pagedResult;
    }

    @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 (!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("13103");
                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("updateAt", 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);
        if (null != fieldsVo) {
            fieldAppliesVo.setApplyType("claim");
        } else {
            fieldAppliesVo.setApplyType("create");
        }
        fieldAppliesVo.setReject("");
        fieldAppliesVo.setUid(uid);
        fieldAppliesVo.setCreatedAt(now);

        // 身份证检查
        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);

        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) {
            queueUtils.sendMsgByRedis(
                    MQConst.MerchantQueue.SQL_MERCHANT_FIELD.getKey(),
                    SqlMapping.gets(toMqSqls, fieldUpdateObjs, fieldApplyInsertObjs)
            );
        } else {
            queueUtils.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("13104");
            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("13104");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        MerchantFieldAppliesVo fieldAppliesVo = fieldAppliesVos.get(idx);
        if (fieldAppliesVo.getApplyStatus() != 2) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13105");
            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);

        // 场地默认验票员 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();
        queueUtils.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("13104");
            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("13104");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        return fieldAppliesVos.get(idx);
    }

    @Override
    public void editIsCheck(String uid, String fieldId, int isCheck) {
        // 当前用户是否管理该场地
        MerchantFieldsVo fieldsVo = this.checkFieldAccount(uid, fieldId);

        // 场地 vo 更改是否自动审核
        LocalDateTime now = LocalDateTime.now();
        fieldsVo.setIsCheck(isCheck > 0 ? 1 : 0);
        fieldsVo.setUpdatedAt(now);

        // 场地 redis
        long s = System.currentTimeMillis();
        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("isCheck", fieldsVo.getIsCheck()).set("updateAt", fieldsVo.getUpdatedAt());
        mongoTemplate.updateFirst(query, update, MerchantFieldsVo.class, MerchantFieldsVo.class.getSimpleName());
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        // 场地 sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldUpdateObjs = CollectionUtil.linkedListObjectArr();
        toMqSqls.add(SqlMapping.get("merchant_fields.update_is_check"));
        fieldUpdateObjs.add(new Object[]{
                fieldsVo.getIsCheck(), fieldsVo.getUpdatedAt(), fieldsVo.getFieldId()
        });

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


    @Override
    public List<MerchantFieldCheckersVo> checkers(String cuid, String fieldId) {
        // 当前用户是否管理该场地
        this.checkFieldAccount(cuid, fieldId);

        List<MerchantFieldCheckersVo> fieldCheckersVos = merchantRdmService.getFieldCheckersVosByFieldId(fieldId);

        return fieldCheckersVos;
    }

    @Override
    public void checkerAdd(String cuid, String fieldId, String uid, String mobile, String name) {
        // 当前用户是否管理该场地
        this.checkFieldAccount(cuid, fieldId);

        // 场地默认验票员，是否已经添加
        List<MerchantFieldCheckersVo> fieldCheckersVos = merchantRdmService.getFieldCheckersVosByFieldId(fieldId);
        if (!CollectionUtils.isEmpty(fieldCheckersVos)) {
            for (MerchantFieldCheckersVo vo : fieldCheckersVos) {
                if (vo.getUid().equals(uid)) {
                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13111");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }
            }
        }

        // 场地默认验票员 vo
        LocalDateTime now = LocalDateTime.now();
        MerchantFieldCheckersVo fieldCheckersVo = MerchantFieldCheckersVo.getNew();
        fieldCheckersVo.setFieldCheckerId(IDGenerator.nextSnowId());
        fieldCheckersVo.setFieldId(fieldId);
        fieldCheckersVo.setUid(uid);
        fieldCheckersVo.setMobile(mobile);
        fieldCheckersVo.setName(name);
        fieldCheckersVo.setCuid(cuid);
        fieldCheckersVo.setCreatedAt(now);

        // 场地默认验票员 redis
        long s = System.currentTimeMillis();
        merchantRdmService.addFieldCheckersVoByFieldId(fieldId, fieldCheckersVos, fieldCheckersVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        // 场地默认验票员 sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldCheckerInsertObjs = CollectionUtil.linkedListObjectArr();
        toMqSqls.add(SqlMapping.get("merchant_field_checkers.insert"));
        fieldCheckerInsertObjs.add(new Object[]{
                fieldCheckersVo.getFieldCheckerId(), fieldCheckersVo.getFieldId(), fieldCheckersVo.getUid(), fieldCheckersVo.getMobile(), fieldCheckersVo.getName(), fieldCheckersVo.getCuid(), fieldCheckersVo.getCreatedAt()
        });

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

    @Override
    public void checkerDel(String cuid, String fieldId, String uid) {
        // 当前用户是否管理该场地
        this.checkFieldAccount(cuid, fieldId);

        // 查找场地默认验票员，是否已经添加
        List<MerchantFieldCheckersVo> fieldCheckersVos = merchantRdmService.getFieldCheckersVosByFieldId(fieldId);
        if (CollectionUtils.isEmpty(fieldCheckersVos)) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13112");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        int idx = IntStream.range(0, fieldCheckersVos.size())
                .filter(i -> fieldCheckersVos.get(i).getUid().equals(uid))
                .findFirst()
                .orElse(-1);
        if (idx < 0) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13112");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        // 场地默认验票员 vo
        LocalDateTime now = LocalDateTime.now();
        MerchantFieldCheckersVo fieldCheckersVo = fieldCheckersVos.get(idx);
        fieldCheckersVo.setCuid(cuid);
        fieldCheckersVo.setUpdatedAt(now);
        fieldCheckersVo.setDeletedAt(now);

        // 场地默认验票员 redis
        long s = System.currentTimeMillis();
        merchantRdmService.delFieldCheckersVoByFieldId(fieldId, fieldCheckersVos, fieldCheckersVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        // 场地默认验票员 sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldCheckerUpdateObjs = CollectionUtil.linkedListObjectArr();
        toMqSqls.add(SqlMapping.get("merchant_field_checkers.update"));
        fieldCheckerUpdateObjs.add(new Object[]{
                fieldCheckersVo.getCuid(), fieldCheckersVo.getUpdatedAt(), fieldCheckersVo.getDeletedAt(), fieldCheckersVo.getFieldCheckerId()
        });

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

    private MerchantFieldsVo checkFieldAccount(String uid, String fieldId) {
        // 当前用户是否管理该场地
        MerchantFieldsVo fieldsVo = merchantRdmService.getFieldsVoByFieldId(fieldId);
        if (null == fieldsVo || !fieldsVo.getUid().equals(uid)) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13101");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        return fieldsVo;
    }
}
