package com.liquidnet.service.slime.service.impl;

import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.*;
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.kylin.dto.vo.mongo.KylinPerformanceVo;
import com.liquidnet.service.slime.constant.SlimeAuthorizationConst;
import com.liquidnet.service.slime.dto.param.SlimeAuthorizationPermissionParam;
import com.liquidnet.service.slime.dto.param.SlimeAuthorizationRecordParam;
import com.liquidnet.service.slime.dto.vo.SlimeAuthorizationRecordsVo;
import com.liquidnet.service.slime.dto.vo.SlimeFieldCheckersVo;
import com.liquidnet.service.slime.dto.vo.SlimeFieldsDetailsVo;
import com.liquidnet.service.slime.dto.vo.SlimeFieldsVo;
import com.liquidnet.service.slime.service.ISlimeFieldsService;
import com.liquidnet.service.slime.service.SlimeMongoService;
import com.liquidnet.service.slime.service.SlimeRdmService;
import com.liquidnet.service.slime.util.ObjectUtil;
import com.liquidnet.service.slime.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.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

@Slf4j
@Service
public class SlimeFieldsServiceImpl implements ISlimeFieldsService {

    @Autowired
    SlimeRdmService slimeRdmService;

    @Autowired
    SlimeMongoService slimeMongoService;

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    QueueUtil queueUtil;

    @Autowired
    SlimeAuthorizationRecordsServiceImpl authorizationRecordsService;

    @Override
    public PagedResult<SlimeFieldsVo> 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, SlimeFieldsVo.class, SlimeFieldsVo.class.getSimpleName());

        // 分页
        PagedResult<SlimeFieldsVo> pagedResult = ObjectUtil.getSlimeFieldsVoPagedResult();
        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<SlimeFieldsVo> fieldsVoList = mongoTemplate.find(query, SlimeFieldsVo.class, SlimeFieldsVo.class.getSimpleName());

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

        return pagedResult;
    }

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

        return fieldsVo;
    }

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

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

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

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

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


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

        List<SlimeFieldCheckersVo> fieldCheckersVos = slimeRdmService.getFieldCheckersVosByFieldId(fieldId);
        if (!CollectionUtil.isEmpty(fieldCheckersVos)) {
            for (SlimeFieldCheckersVo checkersVo : fieldCheckersVos) {
                if (null != checkersVo.getMobile() && checkersVo.getMobile().length() > 4) {
                    checkersVo.setMobile(StringUtil.hiddenMobile(checkersVo.getMobile()));
                }
            }
        }

        return fieldCheckersVos;
    }

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

        // 场地默认验票员，不能授权自己，不能重复添加
        if (cuid.equals(uid)) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13105");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        List<SlimeFieldCheckersVo> fieldCheckersVos = slimeRdmService.getFieldCheckersVosByFieldId(fieldId);
        if (!CollectionUtils.isEmpty(fieldCheckersVos)) {
            for (SlimeFieldCheckersVo 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();
        SlimeFieldCheckersVo fieldCheckersVo = SlimeFieldCheckersVo.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();
        slimeRdmService.addFieldCheckersVoByFieldId(fieldId, fieldCheckersVos, fieldCheckersVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        // 场地默认验票员 mongo
        s = System.currentTimeMillis();
        slimeMongoService.addFieldCheckersVo(fieldCheckersVo);
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        // 场地默认验票员 sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> fieldCheckerInsertObjs = CollectionUtil.linkedListObjectArr();
        toMqSqls.add(SqlMapping.get("slime_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();
        queueUtil.sendMsgByRedis(
                MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
                SqlMapping.gets(toMqSqls, fieldCheckerInsertObjs)
        );
        log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);


        SlimeAuthorizationRecordParam param = SlimeAuthorizationRecordParam.getNew();
        param.setMobile(mobile);
        param.setName(name);
        List<SlimeAuthorizationPermissionParam> list = new ArrayList<>();
        SlimeAuthorizationPermissionParam permissionParam = SlimeAuthorizationPermissionParam.getNew();
        permissionParam.setPermissionId(SlimeAuthorizationConst.PerformancePermission.CHECK.getId());
        list.add(permissionParam);
        param.setPermissions(list);
        param.setUid(uid);
        param.setUidRole("CHECKER");
        List<KylinPerformanceVo> voList = mongoTemplate.find(Query.query(Criteria.where("fieldId").is("654336562571018243092042").and("appStatus").gte(3)), KylinPerformanceVo.class, KylinPerformanceVo.class.getSimpleName());
        for (KylinPerformanceVo item : voList) {
            try {
                param.setPerformanceId(item.getPerformancesId());
                authorizationRecordsService.performanceRecordCheckerAdd(cuid, param);
            } catch (Exception e) {
                continue;
            }
        }

        return fieldCheckersVo.getFieldCheckerId();
    }

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

        // 查找场地默认验票员，是否已经添加
        List<SlimeFieldCheckersVo> fieldCheckersVos = slimeRdmService.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();
        SlimeFieldCheckersVo fieldCheckersVo = fieldCheckersVos.get(idx);
        fieldCheckersVo.setCuid(cuid);
        fieldCheckersVo.setUpdatedAt(now);
        fieldCheckersVo.setDeletedAt(now);

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

        // 场地默认验票员 mongo
        s = System.currentTimeMillis();
        slimeMongoService.delFieldCheckersVoByFieldCheckerId(fieldCheckersVo.getFieldCheckerId());
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

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

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

        List<KylinPerformanceVo> voList = mongoTemplate.find(Query.query(Criteria.where("fieldId").is("654336562571018243092042").and("appStatus").gte(3)), KylinPerformanceVo.class, KylinPerformanceVo.class.getSimpleName());
        for (KylinPerformanceVo item : voList) {
            SlimeAuthorizationRecordsVo vo = mongoTemplate.findOne(Query.query(Criteria.where("performanceId").is(item.getPerformancesId()).and("uidRole").is("CHECKER").and("uid").is(uid).and("cuid").is(cuid)), SlimeAuthorizationRecordsVo.class, SlimeAuthorizationRecordsVo.class.getSimpleName());
            authorizationRecordsService.performanceRecordCheckerDel(cuid, vo.getAuthorizationRecordId());
        }
    }

    @Override
    public SlimeFieldsDetailsVo fieldDetails(String fieldId) {
        SlimeFieldsVo vo = slimeRdmService.getFieldsVoByFieldId(fieldId);
        SlimeFieldsDetailsVo returnData = SlimeFieldsDetailsVo.getNew();
        BeanUtils.copyProperties(vo, returnData);
        return returnData;
    }


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