package net.northking.cctp.flowable.api.implement.service;

import net.northking.cctp.common.cache.EntityNameCache;
import net.northking.cctp.common.cache.EntityNameCatalog;
import net.northking.cctp.common.db.DefaultPagination;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.dto.CpLogAddDto;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.feign.MonsterLogServer;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.common.util.UUIDUtil;
import net.northking.cctp.flowable.bus.dto.ImFlowMQDto;
import net.northking.cctp.flowable.bus.platform.LogPublisher;
import net.northking.cctp.flowable.common.FlowableConstants;
import net.northking.cctp.flowable.common.exception.FlowAppError;
import net.northking.cctp.flowable.db.entity.IpEvent;
import net.northking.cctp.flowable.db.entity.IpProcess;
import net.northking.cctp.flowable.db.entity.IpState;
import net.northking.cctp.flowable.db.entity.SpDefProcess;
import net.northking.cctp.flowable.db.jpa.IpEventRepository;
import net.northking.cctp.flowable.db.jpa.IpProcessRepository;
import net.northking.cctp.flowable.db.jpa.IpStateRepository;
import net.northking.cctp.flowable.dto.ip.Event;
import net.northking.cctp.flowable.dto.ip.IpProcessState;
import net.northking.cctp.flowable.dto.ip.State;
import net.northking.cctp.flowable.sm.Coordinate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class ImplementProcessServiceImpl implements ImplementProcessService {
    /**
     * 日志
     */
    private final static Logger logger = LoggerFactory.getLogger(ImplementProcessServiceImpl.class);

    @Autowired
    private IpProcessRepository processRepository;

    @Autowired
    private IpStateRepository stateRepository;

    @Autowired
    private IpEventRepository eventRepository;

    @Autowired
    private EntityNameCache entityNameCache;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LogPublisher logPublisher;

    /**
     * 保存实施流程
     *
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public String saveProcess(IpProcessState dto) {
        // 名称唯一校验
        duplicateNameCheck(dto);
        // 保存流程
        IpProcess process = new IpProcess();
        BeanUtils.copyProperties(dto, process);
        Optional<IpProcess> dbResult = processRepository.findById(dto.getId());
        if (dbResult.isPresent()) {
            process.setCreatedBy(dbResult.get().getCreatedBy());
            process.setCreatedTime(dbResult.get().getCreatedTime());
        }
        processRepository.save(process);

        // 保存节点信息
        List<IpState> states = new ArrayList<>();
        for (State state : dto.getStates()) {
            IpState tempState = new IpState();
            BeanUtils.copyProperties(state, tempState);
            tempState.setIpId(dto.getId());
            states.add(tempState);
        }
        // 先删除
        IpState delState = new IpState();
        delState.setIpId(dto.getId());
        List<IpState> stateDeleteList = stateRepository.findAll(Example.of(delState));
        stateRepository.deleteAll(stateDeleteList);
        stateRepository.flush();
        // 再插入
        if (!states.isEmpty()) {
            stateRepository.saveAll(states);
        }

        // 保存连线
        List<IpEvent> events = new ArrayList<>();
        for (Event event : dto.getEvents()) {
            IpEvent tempEvent = new IpEvent();
            BeanUtils.copyProperties(event, tempEvent);
            tempEvent.setStartPoint(event.getStartPoint().toString());
            tempEvent.setEndPoint(event.getEndPoint().toString());
            tempEvent.setIpId(dto.getId());
            events.add(tempEvent);
        }
        // 先删除
        IpEvent delEvent = new IpEvent();
        delEvent.setIpId(dto.getId());
        List<IpEvent> eventDeleteList = eventRepository.findAll(Example.of(delEvent));
        eventRepository.deleteAll(eventDeleteList);
        // 再插入
        if (!events.isEmpty()) {
            eventRepository.saveAll(events);
        }
        // 刷新缓存
        putProcessToCache(process, states);
        return dto.getId();
    }

    @Override
    public IpProcessState findByPK(String uuid) {
        Optional<IpProcess> optional = processRepository.findById(uuid);
        if (optional.isPresent()) {
            IpProcess process = optional.get();
            IpProcessState processDto = new IpProcessState();
            BeanUtils.copyProperties(process, processDto);

            // 节点信息
            IpState stateQuery = new IpState();
            stateQuery.setIpId(uuid);
            List<IpState> allStates = stateRepository.findAll(Example.of(stateQuery));
            ArrayList<State> stateList = new ArrayList<>();
            if (!allStates.isEmpty()) {
                for (IpState state : allStates) {
                    State stateDto = new State();
                    BeanUtils.copyProperties(state, stateDto);
                    stateDto.setIpId(uuid);
                    stateList.add(stateDto);
                }
                processDto.setStates(stateList);
            }
            // 连线信息
            IpEvent eventQuery = new IpEvent();
            eventQuery.setIpId(uuid);
            List<IpEvent> allEvents = eventRepository.findAll(Example.of(eventQuery));
            List<Event> eventList = new ArrayList<>();
            if (!allEvents.isEmpty()) {
                for (IpEvent event : allEvents) {
                    Event eventDto = new Event();
                    BeanUtils.copyProperties(event, eventDto);
                    eventDto.setStartPoint(new Coordinate(event.getStartPoint()));
                    eventDto.setEndPoint(new Coordinate(event.getEndPoint()));
                    eventDto.setIpId(uuid);
                    eventList.add(eventDto);
                }
                processDto.setEvents(eventList);
            }
            return processDto;
        }
        return null;
    }

    @Override
    @Transactional
    public Integer deleteByPK(String uuid) {
        Optional<IpProcess> idOptional = processRepository.findById(uuid);
        if (!idOptional.isPresent()) {
            throw new PlatformRuntimeException(FlowAppError.DELETE_FAILED);
        }
        IpProcess result = idOptional.get();

        if (result.getDefaulted()) {
            throw new PlatformRuntimeException(FlowAppError.FLOW_IS_DEFAULTED_NOT_ALLOWED_DELETED);
        }
        Boolean isDelete = result.getDeleted();
        result.setDeleted(true);
        try {
            processRepository.save(result);
            if (!isDelete){
                CpLogAddDto cpLogAddDto = new CpLogAddDto();
                cpLogAddDto.setContent(FlowableConstants.DEL_IMPLEMENT_PROCESS + result.getName());
                cpLogAddDto.setModule(FlowableConstants.IMPLEMENT_PROCESS_MODULE);
                cpLogAddDto.setType(FlowableConstants.LOG_STATUS_DEL);
                MonsterLogServer.sendLogToMonster(cpLogAddDto);
                logPublisher.deployLogContent(cpLogAddDto);
            }
            return 1;
        } catch (Exception e) {
            logger.error("删除失败", e);
        }
        return 0;
    }

    @Override
    @Transactional
    public Integer deleteBatch(List<String> batch) {
        int result = 0;
        for (String one : batch) {
            result += this.deleteByPK(one);
        }
        return result;
    }

    /**
     * 更改流程是否默认
     *
     * @param uuid
     * @return
     */
    @Override
    @Transactional
    public Integer updateDefault(String uuid) {
        try {
            Optional<IpProcess> idOptional = processRepository.findById(uuid);
            if (!idOptional.isPresent()) {
                return 0;
            }
            // 取消现在的
            IpProcess query = new IpProcess();
            query.setDeleted(false);
            query.setDefaulted(true);
            query.setTenantId(NKSecurityContext.getTenantId());
            List<IpProcess> defaultedList = processRepository.findAll(Example.of(query));
            for (IpProcess old : defaultedList) {
                old.setDefaulted(false);
            }
            processRepository.saveAll(defaultedList);
            // 将选中的更新
            IpProcess process = idOptional.get();
            process.setDefaulted(true);
            processRepository.save(process);
            return 1;
        } catch (Exception e) {
            logger.error(FlowAppError.SET_DEFAULTED_IS_FAILED.getMsg(), e);
            throw new PlatformRuntimeException(FlowAppError.SET_DEFAULTED_IS_FAILED);
        }
    }

    @Override
    public Pagination<IpProcessState> pagingQuery(QueryByPage<IpProcess> queryByPage) {
        Pagination<IpProcessState> result = new DefaultPagination<>(queryByPage.getPageNo(), queryByPage.getPageSize());
        ArrayList<IpProcessState> records = new ArrayList<>();
        if (queryByPage.getQuery() == null) {
            queryByPage.setQuery(new IpProcess());
        }
        if (!queryByPage.hasOrderBy()) {
            queryByPage.addQueryOrderByDesc(SpDefProcess.KEY_createdTime);
        }
        queryByPage.getQuery().setDeleted(false);
        //判断name是否为空
        if (StringUtils.isEmpty(queryByPage.getQuery().getName())){
            Pagination<IpProcess> page = processRepository.findAll(queryByPage);
            for (IpProcess record : page.getRecords()) {
                IpProcessState process = new IpProcessState();
                BeanUtils.copyProperties(record, process);
                String cacheCreated = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER, process.getCreatedBy());
                process.setCreatedBy(StringUtils.isEmpty(cacheCreated) ? process.getCreatedBy() : cacheCreated );
                String cacheUpdated = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER, process.getUpdatedBy());
                process.setUpdatedBy(StringUtils.isEmpty(cacheUpdated) ? process.getUpdatedBy() : cacheUpdated);
                records.add(process);
            }
            result.setRecords(records);
            result.setRecordCount(page.getRecordCount());
        }else {
            //模糊查询
            Integer pageNo = (queryByPage.getPageNo() - 1) * queryByPage.getPageSize();
            List<IpProcess> page = processRepository.queryProcess(queryByPage.getQuery().getName(),
                    NKSecurityContext.getTenantId(), queryByPage.getQuery().getDeleted(), pageNo, queryByPage.getPageSize());
            //查询数量
            Integer count = processRepository.countProcess(queryByPage.getQuery().getName(),
                    NKSecurityContext.getTenantId(), queryByPage.getQuery().getDeleted());
            for (IpProcess record : page) {
                IpProcessState process = new IpProcessState();
                BeanUtils.copyProperties(record, process);
                String cacheCreated = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER, process.getCreatedBy());
                process.setCreatedBy(StringUtils.isEmpty(cacheCreated) ? process.getCreatedBy() : cacheCreated );
                String cacheUpdated = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER, process.getUpdatedBy());
                process.setUpdatedBy(StringUtils.isEmpty(cacheUpdated) ? process.getUpdatedBy() : cacheUpdated);
                records.add(process);
            }
            result.setRecords(records);
            result.setRecordCount(count);
            result.setPageNo(queryByPage.getPageNo());
            result.setPageSize(queryByPage.getPageSize());
        }
        return result;
    }

    @Override
    @Transactional
    public void reloadProcessCache() {
        long count = processRepository.count();
        if (count == 0) {
            return;
        }
        QueryByPage<IpProcess> byPage = new QueryByPage<>();
        byPage.setPageSize(500);
        byPage.addQueryOrderByDesc(FlowableConstants.CREATED_TIME);
        for (int i = 0; i <= count / 100; i++) {
            byPage.setPageNo(i + 1);
            Pagination<IpProcess> temp = processRepository.findAll(byPage);
            if (temp.getRecords() != null && !temp.getRecords().isEmpty()) {    // 不为空，放到缓存中
                for (IpProcess record : temp.getRecords()) {
                    if (record != null && record.getStateList() != null && !record.getStateList().isEmpty()) {
                        putProcessToCache(record, record.getStateList());
                    }
                }
            }
        }
    }

    /**
     * 根据参数创建测试实施流程
     *
     * @param imFlow
     */
    @Override
    @Transactional
    public void createProcess(ImFlowMQDto imFlow) {
        if (imFlow.getImMenu() == null || imFlow.getImMenu().isEmpty() || imFlow.getMenuMap() == null || imFlow.getMenuMap().isEmpty() || !imFlow.getMenuMap().containsKey("项目成员管理")) {
            return;
        }
        // 查询租户下是否意见建立了名为：默认测试实施流程
        IpProcess process = new IpProcess();
        process.setName("默认测试实施流程");
        process.setTenantId(NKSecurityContext.getTenantId());
        process.setCreatedBy(NKSecurityContext.getUserId());
        // 删除原来的默认的，
        List<IpProcess> allResult = processRepository.findAll(Example.of(process));

        if (allResult.size() > 0) {
            BeanUtils.copyProperties(allResult.get(0), process);
        } else {
            process.setDeleted(false);
            process.setDefaulted(false);
            process.setEnabled(true);
            process.setCanModify(true);
            process.setId(UUIDUtil.create32UUID());
            process.setMemo("系统默认产生");
        }
        processRepository.save(process);

        // 删除原有的节点和连线
        IpState delState = new IpState();
        delState.setIpId(process.getId());
        List<IpState> allDelStates = stateRepository.findAll(Example.of(delState));
        if (allDelStates != null && !allDelStates.isEmpty()) {
            stateRepository.deleteAll(allDelStates);
            stateRepository.flush();
        }
        IpEvent delEvent = new IpEvent();
        delEvent.setIpId(process.getId());
        List<IpEvent> allDelEvents = eventRepository.findAll(Example.of(delEvent));
        if (allDelEvents != null && !allDelEvents.isEmpty()) {
            eventRepository.delete(delEvent);
            eventRepository.flush();
        }

        // 处理节点和连线
        List<IpState> states = new ArrayList<>();
        List<String> imMenu = imFlow.getImMenu();
        Map<String, String> menuMap = imFlow.getMenuMap();
        int idx = 0;
        for (int i = 0; i < imMenu.size(); i++) {
            String menuName = imMenu.get(i);
            String menuId = menuMap.get(menuName);
            if (StringUtils.isEmpty(menuId)) {
                continue;
            }
            IpState state = new IpState();
            state.setId(UUIDUtil.create32UUID());
            state.setLabel(menuId);
            state.setIpId(process.getId());
            if (i == 0) {
                state.setShape("start-node");
                state.setInitialState(true);
            } else {
                state.setShape("normal-node");
            }
            state.setX(500 + idx * 200);
            state.setY(100);
            states.add(state);
            idx++;
        }

        List<IpEvent> events = new ArrayList<>();
        for (int i = 0; i < states.size(); i++) {
            if (i == states.size() - 1) {
                break;
            }
            IpState startState = states.get(i);
            IpState endState = states.get(i + 1);
            IpEvent event = new IpEvent();
            event.setId(UUIDUtil.create32UUID());
            event.setIpId(process.getId());
            event.setSource(startState.getId());
            event.setTarget(endState.getId());
            event.setShape("event-line");
            event.setVisible(true);
            int startPointX = startState.getX() + 60;
            int startPointY = startState.getY();
            int endPointX = endState.getX() - 60;
            int endPointY = endState.getY();
            event.setStartPoint(startPointX + "," + startPointY);    // 待处理
            event.setEndPoint(endPointX + "," + endPointY);      // 待处理
            events.add(event);
        }
        stateRepository.saveAll(states);
        eventRepository.saveAll(events);
    }

    /**
     *
     * @param record
     * @param states
     */
    private void putProcessToCache(IpProcess record, List<IpState> states) {
        List<String> stateList = new ArrayList<>();
        for (IpState state : states) {
            stateList.add(state.getLabel());
        }
        redisTemplate.opsForValue().set(FlowableConstants.IMPLEMENT_PREFIX + record.getId(), stateList);
    }

    /**
     * 重名检查
     *
     * @param dto
     */
    private void duplicateNameCheck(IpProcessState dto) {
        IpProcess temp = new IpProcess();
        temp.setTenantId(NKSecurityContext.getTenantId());
        temp.setName(dto.getName());
        temp.setDeleted(false);
        List<IpProcess> nameList = processRepository.findAll(Example.of(temp));
        if (nameList.size() > 0
                && nameList.get(0).getName().equals(dto.getName())
                && !nameList.get(0).getId().equals(dto.getId())) {
            throw new PlatformRuntimeException(FlowAppError.FLOW_NAME_IS_DULICATED);
        }
    }

}
