package com.yunzhupaas.flowable.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.yunzhupaas.base.service.SuperServiceImpl;
import com.yunzhupaas.flowable.entity.*;
import com.yunzhupaas.flowable.enums.RecordEnum;
import com.yunzhupaas.flowable.enums.TaskStatusEnum;
import com.yunzhupaas.flowable.mapper.RecordMapper;
import com.yunzhupaas.flowable.model.operator.OperatorVo;
import com.yunzhupaas.flowable.model.record.RecordVo;
import com.yunzhupaas.flowable.model.task.TaskPagination;
import com.yunzhupaas.flowable.model.util.FlowNature;
import com.yunzhupaas.flowable.service.DelegateService;
import com.yunzhupaas.flowable.service.RecordService;
import com.yunzhupaas.flowable.service.TaskService;
import com.yunzhupaas.flowable.util.RecordUtil;
import com.yunzhupaas.util.RandomUtil;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.UserProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 类的描述
 *
 * @author YUNZHUPAASYUNZHUPAAS开发组
 * @version 5.0.x
 * @since 2024/4/23 9:22
 */
@Service
public class RecordServiceImpl extends SuperServiceImpl<RecordMapper, RecordEntity> implements RecordService {
    @Autowired
    private TaskService taskService;
    @Autowired
    private RecordUtil recordUtil;
    @Autowired
    private DelegateService delegateService;

    @Override
    public List<RecordEntity> getList(String taskId) {
        QueryWrapper<RecordEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(RecordEntity::getTaskId, taskId).orderByAsc(RecordEntity::getHandleTime);
        return this.list(queryWrapper);
    }

    @Override
    public List<RecordEntity> getRecordList(String taskId, List<Integer> statusList) {
        QueryWrapper<RecordEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(RecordEntity::getTaskId, taskId);
        if (CollectionUtil.isNotEmpty(statusList)) {
            queryWrapper.lambda().in(RecordEntity::getHandleType, statusList);
        }
        queryWrapper.lambda().orderByAsc(RecordEntity::getHandleTime);
        return this.list(queryWrapper);
    }

    @Override
    public List<OperatorVo> getList(TaskPagination pagination) {
        String loginUserId = UserProvider.getLoginUserId();
        if (StringUtil.isNotEmpty(pagination.getUserId())) {
            loginUserId = pagination.getUserId();
        }
        Integer[] handleStatus = new Integer[] { 0, 1, 3, 5, 7, 18 };
        MPJLambdaWrapper<RecordEntity> recordWrapper = JoinWrappers.lambda(RecordEntity.class)
                .leftJoin(TaskEntity.class, TaskEntity::getId, RecordEntity::getTaskId)
                .select(RecordEntity::getHandleId)
                .select(RecordEntity::getNodeCode)
                .select(RecordEntity::getTaskId)
                .selectMax(RecordEntity::getHandleTime)
                .in(RecordEntity::getHandleType, handleStatus)
                .isNotNull(RecordEntity::getOperatorId)
                .groupBy(RecordEntity::getTaskId, RecordEntity::getNodeCode, RecordEntity::getHandleId);

        List<DelegateEntity> delegateList = delegateService.getByToUserId(loginUserId);
        String finalLoginUserId = loginUserId;
        recordWrapper.and(t -> {
            t.eq(RecordEntity::getHandleId, finalLoginUserId);
            for (DelegateEntity delegate : delegateList) {
                if (StringUtil.isNotEmpty(delegate.getFlowId())) {
                    String[] flowIds = delegate.getFlowId().split(",");
                    t.or(tw -> tw.in(TaskEntity::getTemplateId, flowIds)
                            .eq(RecordEntity::getHandleId, delegate.getUserId())
                            .eq(RecordEntity::getCreatorUserId, finalLoginUserId)
                            .between(RecordEntity::getHandleTime, delegate.getStartTime(), delegate.getEndTime()));
                } else {
                    t.or(t1 -> t1.eq(RecordEntity::getHandleId, delegate.getUserId())
                            .eq(RecordEntity::getCreatorUserId, finalLoginUserId)
                            .between(RecordEntity::getHandleTime, delegate.getStartTime(), delegate.getEndTime()));
                }
            }
        });

        List<RecordEntity> recordList = this.selectJoinList(RecordEntity.class, recordWrapper);

        if (recordList.isEmpty()) {
            return new ArrayList<>();
        }
        List<String> taskId = recordList.stream().map(RecordEntity::getTaskId).collect(Collectors.toList());
        List<String> nodeCode = recordList.stream().map(RecordEntity::getNodeCode).collect(Collectors.toList());
        List<Date> handleTime = recordList.stream().map(RecordEntity::getHandleTime).collect(Collectors.toList());
        MPJLambdaWrapper<TaskEntity> wrapper = JoinWrappers.lambda(TaskEntity.class)
                .leftJoin(RecordEntity.class, RecordEntity::getTaskId, TaskEntity::getId)
                .leftJoin(OperatorEntity.class, OperatorEntity::getId, RecordEntity::getOperatorId)
                .selectAll(TaskEntity.class)
                .selectAs(TaskEntity::getUrgent, OperatorVo::getFlowUrgent)
                .selectAs(RecordEntity::getId, OperatorVo::getId)
                .selectAs(RecordEntity::getNodeName, OperatorVo::getCurrentNodeName)
                .selectAs(RecordEntity::getNodeCode, OperatorVo::getNodeCode)
                .selectAs(RecordEntity::getHandleType, OperatorVo::getStatus)
                .selectAs(RecordEntity::getHandleId, OperatorVo::getHandleId)
                .selectAs(RecordEntity::getHandleTime, OperatorVo::getCreatorTime)
                .selectAs(OperatorEntity::getHandleId, OperatorVo::getOperatorHandleId)
                .selectAs(RecordEntity::getTaskId, OperatorVo::getTaskId)
                .selectAs(RecordEntity::getCreatorUserId, OperatorVo::getRecordCreatorUserId)
                .selectAs(TaskEntity::getCreatorUserId, OperatorVo::getCreatorUserId)
                .in(RecordEntity::getTaskId, taskId)
                .in(RecordEntity::getNodeCode, nodeCode)
                .ne(TaskEntity::getStatus, TaskStatusEnum.CANCEL.getCode());

        if (CollectionUtil.isNotEmpty(handleTime)) {
            wrapper.and(e -> {
                for (Date date : handleTime) {
                    Calendar start = Calendar.getInstance();
                    start.setTime(date);
                    start.set(Calendar.MILLISECOND, 0);
                    Calendar end = Calendar.getInstance();
                    end.setTime(date);
                    end.set(Calendar.MILLISECOND, 999);
                    e.or(t -> t.between(RecordEntity::getHandleTime, start.getTime(), end.getTime()));
                }
            });
        }

        Integer status = pagination.getStatus();
        if (null != status) {
            switch (status) {
                case 1:
                    // 同意
                    wrapper.eq(RecordEntity::getHandleType, RecordEnum.audit.getCode());
                    break;
                case 2:
                    // 拒绝
                    wrapper.eq(RecordEntity::getHandleType, RecordEnum.reject.getCode());
                    break;
                case 3:
                    // 转审
                    List<Integer> transferList = ImmutableList.of(RecordEnum.transfer.getCode(),
                            RecordEnum.transferProcessing.getCode());
                    wrapper.in(RecordEntity::getHandleType, transferList);
                    break;
                case 4:
                    // 加签
                    wrapper.eq(RecordEntity::getHandleType, RecordEnum.addSign.getCode());
                    break;
                case 5:
                    // 退回
                    wrapper.eq(RecordEntity::getHandleType, RecordEnum.back.getCode());
                    break;
            }
        }
        String keyWord = pagination.getKeyword();
        if (ObjectUtil.isNotEmpty(keyWord)) {
            wrapper.and(t -> t.like(TaskEntity::getEnCode, keyWord).or().like(TaskEntity::getFullName, keyWord));
        }
        // 所属分类
        String category = pagination.getFlowCategory();
        if (ObjectUtil.isNotEmpty(category)) {
            wrapper.in(TaskEntity::getFlowCategory, category.split(","));
        }
        // 所属流程
        String templateId = pagination.getTemplateId();
        if (ObjectUtil.isNotEmpty(templateId)) {
            wrapper.eq(TaskEntity::getTemplateId, templateId);
        }
        // 紧急程度
        Integer flowUrgent = pagination.getFlowUrgent();
        if (ObjectUtil.isNotEmpty(flowUrgent)) {
            wrapper.eq(TaskEntity::getUrgent, flowUrgent);
        }
        // 发起人员
        String creatorUserId = pagination.getCreatorUserId();
        if (ObjectUtil.isNotEmpty(creatorUserId)) {
            wrapper.eq(TaskEntity::getCreatorUserId, creatorUserId);
        }
        // 日期范围（近7天、近1月、近3月、自定义）
        if (ObjectUtil.isNotEmpty(pagination.getStartTime()) && ObjectUtil.isNotEmpty(pagination.getEndTime())) {
            wrapper.between(TaskEntity::getStartTime, new Date(pagination.getStartTime()),
                    new Date(pagination.getEndTime()));
        }
        wrapper.orderByDesc(RecordEntity::getHandleTime);

        Page<OperatorVo> page = new Page<>(pagination.getCurrentPage(), pagination.getPageSize());
        Page<OperatorVo> data = taskService.selectJoinListPage(page, OperatorVo.class, wrapper);
        for (OperatorVo record : data.getRecords()) {
            if (!ObjectUtil.equals(record.getRecordCreatorUserId(), record.getHandleId())) {
                boolean isUser = ObjectUtil.equals(record.getHandleId(), loginUserId)
                        && ObjectUtil.equals(record.getOperatorHandleId(), record.getRecordCreatorUserId());
                record.setDelegateUser(!isUser ? record.getRecordCreatorUserId() : null);
            }
        }
        return pagination.setData(data.getRecords(), page.getTotal());
    }

    @Override
    public RecordEntity getInfo(String id) {
        if (StringUtil.isBlank(id)) {
            return null;
        }
        return this.getById(id);
    }

    @Override
    public void create(RecordEntity entity) {
        entity.setId(RandomUtil.uuId());
        this.save(entity);
    }

    @Override
    public void update(String id, RecordEntity entity) {
        entity.setId(id);
        this.updateById(entity);
    }

    @Override
    public void updateStatusToInvalid(String taskId, List<String> nodeCodeList) {
        UpdateWrapper<RecordEntity> wrapper = new UpdateWrapper<>();
        wrapper.lambda().eq(RecordEntity::getTaskId, taskId);
        if (CollectionUtil.isNotEmpty(nodeCodeList)) {
            wrapper.lambda().in(RecordEntity::getNodeCode, nodeCodeList);
        }
        wrapper.lambda().set(RecordEntity::getStatus, FlowNature.Invalid);
        this.update(wrapper);
    }

    @Override
    public List<RecordVo> getList(String taskId, String nodeId) {
        QueryWrapper<RecordEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(RecordEntity::getTaskId, taskId).like(RecordEntity::getNodeId, nodeId)
                .orderByDesc(RecordEntity::getHandleTime).orderByDesc(RecordEntity::getId);
        List<RecordEntity> list = this.list(queryWrapper);
        return recordUtil.getRecordList(list);
    }

}
