package cn.axzo.msg.center.dal;

import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.dal.mapper.TodoMapper;
import cn.axzo.msg.center.domain.entity.Todo;
import cn.axzo.msg.center.domain.entity.TodoBusiness;
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.enums.TodoType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static java.util.stream.Collectors.toSet;

/**
 * @author yanglin
 */
@Slf4j
@Component
public class TodoDao extends ServiceImpl<TodoMapper, Todo> {

    public List<Todo> getByCodes(List<String> codes) {
        if (CollectionUtils.isEmpty(codes))
            return Collections.emptyList();
        return lambdaQuery()
                .in(Todo::getIdentityCode, codes)
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
    }

    public List<Todo> getToBeDone(Long personId, @Nullable Long ouId) {
        return lambdaQuery()
                .eq(Todo::getExecutorPersonId, personId)
                .eq(ouId != null, Todo::getOuId, ouId)
                .eq(Todo::getType, TodoType.EXECUTABLE)
                .eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT)
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
    }

    public Optional<Todo> findTodoByCode(String todoCode) {
        if (StringUtils.isBlank(todoCode))
            return Optional.empty();
        Todo todo = lambdaQuery()
                .eq(Todo::getIdentityCode, todoCode)
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .last("LIMIT 1")
                .one();
        return Optional.ofNullable(todo);
    }

    public List<Todo> getBySubBizCodes(List<String> subBizCodes, boolean advanceableOnly) {
        if (CollectionUtils.isEmpty(subBizCodes))
            return Collections.emptyList();
        return lambdaQuery()
                .in(Todo::getSubBizCode, subBizCodes)
                .in(advanceableOnly, Todo::getState,
                        PendingMessageStateEnum.HAS_BEEN_SENT, PendingMessageStateEnum.PROCESSING)
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
    }

    public List<Todo> getBySubBizCode(String subBizCode) {
        return lambdaQuery()
                .eq(Todo::getSubBizCode, subBizCode)
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
    }

    public List<Todo> getByIdentityCodes(List<String> identityCodes) {
        return lambdaQuery()
                .eq(Todo::getIdentityCode, identityCodes)
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
    }

    public Optional<Todo> getLatestByBiz(
            @Nullable String templateCode, String bizCode, String subBizCode, TodoType todoType) {
        if (StringUtils.isBlank(bizCode) && StringUtils.isBlank(subBizCode))
            return Optional.empty();
        Todo todo = lambdaQuery()
                .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .eq(todoType != null, Todo::getType, todoType)
                .eq(StringUtils.isNotBlank(templateCode), Todo::getTemplateCode, templateCode)
                .eq(StringUtils.isNotBlank(bizCode), Todo::getBizCode, bizCode)
                .eq(StringUtils.isNotBlank(subBizCode), Todo::getSubBizCode, subBizCode)
                .orderByDesc(Todo::getId)
                .last("LIMIT 1")
                .one();
        return Optional.ofNullable(todo);
    }

    public SampleTodos getSampleTodos(List<TodoBusiness> businesses) {
        if (CollectionUtils.isEmpty(businesses))
            return new SampleTodos(Collections.emptyList());
        Set<String> sampleCodes = businesses.stream()
                .map(TodoBusiness::getSampleTodoCode)
                .collect(toSet());
        List<Todo> todos = lambdaQuery()
                .in(Todo::getIdentityCode, sampleCodes)
                .list();
        return new SampleTodos(todos);
    }

    public SampleTodos getLatestExecutableTodos(List<TodoBusiness> businesses) {
        Set<Long> businessIds = businesses.stream()
                .map(BaseEntityExt::getId)
                .collect(toSet());
        if (businesses.isEmpty())
            return new SampleTodos(Collections.emptyList());
        LambdaQueryWrapper<Todo> groupQuery = new QueryWrapper<Todo>()
                .select("max(id) as id, todo_business_id")
                .groupBy("todo_business_id")
                .lambda()
                .in(Todo::getTodoBusinessId, businessIds)
                .eq(Todo::getType, TodoType.EXECUTABLE);
        Set<Long> ids = getBaseMapper().selectList(groupQuery).stream()
                .map(BaseEntityExt::getId)
                .collect(toSet());
        if (CollectionUtils.isEmpty(ids))
            return new SampleTodos(Collections.emptyList());
        List<Todo> todos = listByIds(ids);
        return new SampleTodos(todos);
    }

    public Boolean batchSetExecutableProcessing(List<String> identityCodes, List<String> subBizCodes) {
        if (CollectionUtils.isEmpty(identityCodes) && CollectionUtils.isEmpty(subBizCodes)) {
            throw new IllegalArgumentException("identityCodes and subBizCodes can't be empty");
        }
        return lambdaUpdate()
                .eq(Todo::getType, TodoType.EXECUTABLE)
                .in(CollectionUtils.isNotEmpty(identityCodes), Todo::getIdentityCode, identityCodes)
                .in(CollectionUtils.isNotEmpty(subBizCodes), Todo::getSubBizCode, subBizCodes)
                .in(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT, PendingMessageStateEnum.PROCESSING)
                .set(Todo::getState, PendingMessageStateEnum.PROCESSING)
                .update();
    }

    public boolean setExecutableHide(String subBizCode, Date expireTime) {
        return lambdaUpdate()
                .eq(Todo::getType, TodoType.EXECUTABLE)
                .eq(Todo::getSubBizCode, subBizCode)
                .eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT)
                .set(Todo::getHideUntil, expireTime)
                .update();
    }
}