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.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.merchant.constant.MerchantAuthorizationConst;
import com.liquidnet.service.merchant.dto.param.MerchantAuthorizationPermissionParam;
import com.liquidnet.service.merchant.dto.param.MerchantAuthorizationRecordParam;
import com.liquidnet.service.merchant.dto.vo.MerchantAuthorizationPerformanceVo;
import com.liquidnet.service.merchant.dto.vo.MerchantAuthorizationPermissionsVo;
import com.liquidnet.service.merchant.dto.vo.MerchantAuthorizationRecordsVo;
import com.liquidnet.service.merchant.service.IMerchantAuthorizationRecordsService;
import com.liquidnet.service.merchant.service.MerchantMongoService;
import com.liquidnet.service.merchant.util.QueueUtil;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
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 java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

@Slf4j
@Service
public class MerchantAuthorizationRecordsServiceImpl implements IMerchantAuthorizationRecordsService {

    @Autowired
    MerchantMongoService merchantMongoService;

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    MongoConverter mongoConverter;

    @Autowired
    QueueUtil queueUtils;

    @Override
    public String performanceCheckerAdd(String cuid, MerchantAuthorizationRecordParam parameter) {
        LocalDateTime now = LocalDateTime.now();

        // 目前仅授权 验票 统计
        if (cuid.equals(parameter.getUid())) {
            // 无授权权限
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13301");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        MerchantAuthorizationPermissionParam checkPermissionParam = null;
        MerchantAuthorizationPermissionParam salesPermissionParam = null;
        for (MerchantAuthorizationPermissionParam permissionParam: parameter.getPermissions()) {
            if (permissionParam.getPermissionId().equals(MerchantAuthorizationConst.PerformancePermission.CHECK.getId())) {
                checkPermissionParam = permissionParam;
            }
            if (permissionParam.getPermissionId().equals(MerchantAuthorizationConst.PerformancePermission.SALES.getId())) {
                salesPermissionParam = permissionParam;
            }
        }
        if (null == checkPermissionParam && null == salesPermissionParam) {
            // 无授权权限
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13301");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        // 获取最大权限角色
        MerchantAuthorizationConst.PerformanceRole maxPerformanceRole = this.getMaxPerformanceRole(cuid, parameter.getPerformanceId());
        if (null == maxPerformanceRole || maxPerformanceRole.getLevel() <= MerchantAuthorizationConst.PerformanceRole.CHECKER.getLevel()) {
            // 无授权权限
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13301");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        if (null != salesPermissionParam && maxPerformanceRole.getLevel() <= MerchantAuthorizationConst.PerformanceRole.FIELDER.getLevel()) {
            // 无权限授于统计权限
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13302");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        // 验票员 授权记录 vo
        MerchantAuthorizationRecordsVo checkerAuthorizationRecordsVo = MerchantAuthorizationRecordsVo.getNew();
        checkerAuthorizationRecordsVo.setAuthorizationRecordId(IDGenerator.nextSnowId());
        checkerAuthorizationRecordsVo.setPerformanceId(parameter.getPerformanceId());
        checkerAuthorizationRecordsVo.setUidRole(MerchantAuthorizationConst.PerformanceRole.CHECKER.getRole());
        checkerAuthorizationRecordsVo.setUid(parameter.getUid());
        checkerAuthorizationRecordsVo.setMobile(parameter.getMobile());
        checkerAuthorizationRecordsVo.setName(parameter.getName());
        checkerAuthorizationRecordsVo.setCuid(cuid);
        checkerAuthorizationRecordsVo.setCuidRole(maxPerformanceRole.getRole());
        checkerAuthorizationRecordsVo.setCreatedAt(now);

        // 验票员 授权权限 vos
        ArrayList<MerchantAuthorizationPermissionsVo> checkerAuthorizationPermissionsVos = new ArrayList<>();
        if (null != checkPermissionParam) {
            MerchantAuthorizationPermissionsVo checkAuthorizationPermissionsVo = MerchantAuthorizationPermissionsVo.getNew();
            checkAuthorizationPermissionsVo.setAuthorizationPermissionId(IDGenerator.nextSnowId());
            checkAuthorizationPermissionsVo.setAuthorizationRecordId(checkerAuthorizationRecordsVo.getAuthorizationRecordId());
            checkAuthorizationPermissionsVo.setPermissionId(MerchantAuthorizationConst.PerformancePermission.CHECK.getId());
            checkAuthorizationPermissionsVo.setStartTime(now);
            checkAuthorizationPermissionsVo.setEndTime(now.plusYears(10));
            checkAuthorizationPermissionsVo.setCreatedAt(now);
            checkerAuthorizationPermissionsVos.add(checkAuthorizationPermissionsVo);
        }
        if (null != salesPermissionParam) {
            LocalDateTime startTime = LocalDateTime.parse(salesPermissionParam.getStartTime(), DateTimeFormatter.ofPattern(DateUtil.DATE_FULL_STR));
            LocalDateTime endTime = LocalDateTime.parse(salesPermissionParam.getEndTime(), DateTimeFormatter.ofPattern(DateUtil.DATE_FULL_STR));
            if (!startTime.isBefore(endTime)) {
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13303");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            }
            MerchantAuthorizationPermissionsVo salesAuthorizationPermissionsVo = MerchantAuthorizationPermissionsVo.getNew();
            salesAuthorizationPermissionsVo.setAuthorizationPermissionId(IDGenerator.nextSnowId());
            salesAuthorizationPermissionsVo.setAuthorizationRecordId(checkerAuthorizationRecordsVo.getAuthorizationRecordId());
            salesAuthorizationPermissionsVo.setPermissionId(MerchantAuthorizationConst.PerformancePermission.SALES.getId());
            salesAuthorizationPermissionsVo.setStartTime(startTime);
            salesAuthorizationPermissionsVo.setEndTime(endTime);
            salesAuthorizationPermissionsVo.setCreatedAt(now);
            checkerAuthorizationPermissionsVos.add(salesAuthorizationPermissionsVo);
        }
        checkerAuthorizationRecordsVo.setPermissionsVos(checkerAuthorizationPermissionsVos);

        // mongo
        long s = System.currentTimeMillis();
        mongoTemplate.insert(checkerAuthorizationRecordsVo, MerchantAuthorizationRecordsVo.class.getSimpleName());
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        // 聚合角色及权限
        this.syncAuthorizationPerformanceVo(parameter.getPerformanceId(), checkerAuthorizationRecordsVo.getUid());

        // sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> authorizationRecordsInsertObjs = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> authorizationPermissionsInsertObjs = CollectionUtil.linkedListObjectArr();
        // 授权记录 sql
        toMqSqls.add(SqlMapping.get("merchant_authorization_records.insert"));
        authorizationRecordsInsertObjs.add(new Object[]{
                checkerAuthorizationRecordsVo.getAuthorizationRecordId(), checkerAuthorizationRecordsVo.getPerformanceId(),
                checkerAuthorizationRecordsVo.getUidRole(), checkerAuthorizationRecordsVo.getUid(), checkerAuthorizationRecordsVo.getMobile(), checkerAuthorizationRecordsVo.getName(),
                checkerAuthorizationRecordsVo.getCuidRole(), checkerAuthorizationRecordsVo.getCuid(),
                checkerAuthorizationRecordsVo.getCreatedAt()
        });
        // 授权权限 sql
        toMqSqls.add(SqlMapping.get("merchant_authorization_permissions.insert"));
        for (MerchantAuthorizationPermissionsVo authorizationPermissionsVo  : checkerAuthorizationPermissionsVos) {
            authorizationPermissionsInsertObjs.add(new Object[]{
                    authorizationPermissionsVo.getAuthorizationPermissionId(), authorizationPermissionsVo.getAuthorizationRecordId(),
                    authorizationPermissionsVo.getPermissionId(), authorizationPermissionsVo.getStartTime(), authorizationPermissionsVo.getEndTime(),
                    authorizationPermissionsVo.getCreatedAt()
            });
        }

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

        return checkerAuthorizationRecordsVo.getAuthorizationRecordId();
    }

    @Override
    public void performanceCheckerDel(String cuid, String authorizationRecordId) {
        LocalDateTime now = LocalDateTime.now();

        // 查询授权记录
        MerchantAuthorizationRecordsVo authorizationRecordsVo = merchantMongoService.getAuthorizationRecordsVoByAuthorizationRecordId(authorizationRecordId);
        if (null == authorizationRecordsVo || !authorizationRecordsVo.getCuid().equals(cuid) || !authorizationRecordsVo.getUidRole().equals(MerchantAuthorizationConst.PerformanceRole.CHECKER.getRole())) {
            // 无授权权限
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("13301");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }

        // 授权记录 vo
        authorizationRecordsVo.setUpdatedAt(now);
        authorizationRecordsVo.setDeletedAt(now);

        // mongo
        long s = System.currentTimeMillis();
        mongoTemplate.remove(Query.query(Criteria.where("authorizationRecordId").is(authorizationRecordId)), MerchantAuthorizationRecordsVo.class.getSimpleName());
        log.debug("#MONGO耗时:{}ms", System.currentTimeMillis() - s);

        // 聚合角色及权限
        this.syncAuthorizationPerformanceVo(authorizationRecordsVo.getPerformanceId(), authorizationRecordsVo.getUid());

        // sql
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> authorizationRecordsUpdateObjs = CollectionUtil.linkedListObjectArr();
        toMqSqls.add(SqlMapping.get("merchant_authorization_records.update"));
        authorizationRecordsUpdateObjs.add(new Object[]{
                authorizationRecordsVo.getUpdatedAt(), authorizationRecordsVo.getDeletedAt(), authorizationRecordsVo.getAuthorizationRecordId()
        });

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

    @Override
    public List<MerchantAuthorizationRecordsVo> performanceCheckers(String cuid, String performanceId) {
        List<MerchantAuthorizationRecordsVo> authorizationRecordsVos = merchantMongoService.getAuthorizationRecordsCheckersVosByCuid(cuid, performanceId);
        return authorizationRecordsVos;
    }


    private MerchantAuthorizationConst.PerformanceRole getMaxPerformanceRole(String uid, String performanceId) {
        MerchantAuthorizationPerformanceVo authorizationPerformanceVo = mongoTemplate.findOne(Query.query(Criteria.where("performanceId").is(performanceId).and("uid").is(uid)), MerchantAuthorizationPerformanceVo.class, MerchantAuthorizationPerformanceVo.class.getSimpleName());
        if (null == authorizationPerformanceVo) {
            return null;
        }
        if (!CollectionUtil.isEmpty(authorizationPerformanceVo.getUidRoles())) {
            return null;
        }

        MerchantAuthorizationConst.PerformanceRole maxRole = MerchantAuthorizationConst.PerformanceRole.valueOf(authorizationPerformanceVo.getUidRoles().get(0));
        for (String uidRole : authorizationPerformanceVo.getUidRoles()) {
            MerchantAuthorizationConst.PerformanceRole role = MerchantAuthorizationConst.PerformanceRole.valueOf(uidRole);
            if (role.getLevel() > maxRole.getLevel()) {
                maxRole = role;
            }
        }

        return maxRole;
    }

    private void syncAuthorizationPerformanceVo(String performanceId, String uid) {
        List<MerchantAuthorizationRecordsVo> authorizationRecordsVos = merchantMongoService.getAuthorizationRecordsVosByUid(uid, performanceId);

        List<String> uidRoles = new ArrayList<>();
        List<String> permissionIds = new ArrayList<>();

        for (MerchantAuthorizationRecordsVo authorizationRecordsVo: authorizationRecordsVos) {
            if (!uidRoles.contains(authorizationRecordsVo.getUidRole())) {
                uidRoles.add(authorizationRecordsVo.getUidRole());
            }
            for (MerchantAuthorizationPermissionsVo authorizationPermissionsVo: authorizationRecordsVo.getPermissionsVos()) {
                if (!permissionIds.contains(authorizationPermissionsVo.getPermissionId())) {
                    permissionIds.add(authorizationPermissionsVo.getPermissionId());
                }
            }
        }

        // 聚合角色及权限
        MerchantAuthorizationPerformanceVo vo = MerchantAuthorizationPerformanceVo.getNew();
        vo.setUid(uid);
        vo.setPerformanceId(performanceId);
        vo.setUidRoles(uidRoles);
        vo.setPermissionIds(permissionIds);

        Query query = Query.query(Criteria.where("performanceId").is(performanceId).and("uid").is(uid));
        Document document = (Document)mongoConverter.convertToMongoType(vo);
        Update update = Update.fromDocument(document);
        mongoTemplate.upsert(query, update, MerchantAuthorizationPerformanceVo.class, MerchantAuthorizationPerformanceVo.class.getSimpleName());
    }
}
