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

import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
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.MerchantFieldEditParam;
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.MerchantMongoService;
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
    MerchantMongoService merchantMongoService;

    @Autowired
    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("uid")
                    .include("isCheck")
                    .include("name")
                    .include("logo")
                    .include("background")
                    .include("provinceId")
                    .include("provinceName")
                    .include("cityId")
                    .include("cityName")
                    .include("districtId")
                    .include("districtName")
                    .include("address");

            // 查询分页
            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 MerchantFieldsVo fieldInfo(String uid, String fieldId) {
        // 当前用户是否管理该场地
        MerchantFieldsVo fieldsVo = this.checkFieldAccount(uid, fieldId);

        return fieldsVo;
    }

    @Override
    public void editBaseInfo(String uid, MerchantFieldEditParam parameter) {
        // 当前用户是否管理该场地
        MerchantFieldsVo fieldsVo = this.checkFieldAccount(uid, parameter.getFieldId());

        LocalDateTime now = LocalDateTime.now();
        BeanUtils.copyProperties(parameter, fieldsVo);
        fieldsVo.setUpdatedAt(now);

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

        // 场地 mongo
        s = System.currentTimeMillis();
        merchantMongoService.setFieldsVoByFieldId(fieldsVo.getFieldId(), fieldsVo);
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldUpdateObjs = CollectionUtil.linkedListObjectArr();
        // 场地 sql
        toMqSqls.add(SqlMapping.get("merchant_fields.update_base_info"));
        fieldUpdateObjs.add(new Object[]{
                fieldsVo.getName(), fieldsVo.getLogo(), fieldsVo.getBackground(), fieldsVo.getDescription(), fieldsVo.getBuiltDate(),
                fieldsVo.getProvinceId(), fieldsVo.getProvinceName(), fieldsVo.getCityId(), fieldsVo.getCityName(), fieldsVo.getDistrictId(), fieldsVo.getDistrictName(), fieldsVo.getAddress(), fieldsVo.getLongitude(), fieldsVo.getLatitude(),
                fieldsVo.getContactName(), fieldsVo.getContactEmail(),
                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 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);

        // 场地默认验票员，是否已经添加
        if (cuid.equals(uid)) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13103");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        List<MerchantFieldCheckersVo> fieldCheckersVos = merchantRdmService.getFieldCheckersVosByFieldId(fieldId);
        if (!CollectionUtils.isEmpty(fieldCheckersVos)) {
            for (MerchantFieldCheckersVo vo : fieldCheckersVos) {
                if (vo.getUid().equals(uid)) {
                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13103");
                    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("13104");
            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("13104");
            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 || !uid.equals(fieldsVo.getUid())) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13101");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        return fieldsVo;
    }
}
