package cn.axzo.msg.center.message.service.todo.pagequery;

import cn.axzo.msg.center.domain.entity.Todo;
import cn.axzo.msg.center.domain.entity.TodoBusiness;
import cn.axzo.msg.center.service.dto.QueryOrderByDTO;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;

import java.util.Collection;
import java.util.stream.Stream;

/**
 * @author yanglin
 */
public abstract class PageQuerySort<T> {

    public static PageQuerySort<TodoBusiness> TODO_BUSINESS = new PageQuerySort<TodoBusiness>() {

        @Override
        protected void appendDefaultSortExpr(
                PendingMessagePageRequest req,
                LambdaQueryWrapper<TodoBusiness> query) {
            query.orderByDesc(TodoBusiness::getCreateAt);
        }

        @Override
        protected SFunction<TodoBusiness, ?> getCustomSortField(SortField field) {
            if (field == SortField.CREATE_AT)
                return TodoBusiness::getCreateAt;
            else if (field == SortField.UPDATE_AT)
                return TodoBusiness::getUpdateAt;
            else if (field == SortField.ID)
                return TodoBusiness::getId;
            else
                return null;
        }
    };

    public static PageQuerySort<Todo> TODO = new PageQuerySort<Todo>() {

        @Override
        protected void appendDefaultSortExpr(
                PendingMessagePageRequest req,
                LambdaQueryWrapper<Todo> query) {
            if (req.getMsgState() == PendingMessageStateEnum.COMPLETED)
                query.orderByDesc(Todo::getUpdateAt);
            else
                query.orderByDesc(Todo::getCreateAt);
        }

        @Override
        protected SFunction<Todo, ?> getCustomSortField(SortField field) {
            if (field == SortField.CREATE_AT)
                return Todo::getCreateAt;
            else if (field == SortField.UPDATE_AT)
                return Todo::getUpdateAt;
            else if (field == SortField.ID)
                return Todo::getId;
            else
                return null;
        }

    };

    public final void appendSortExpr(
            PendingMessagePageRequest req, LambdaQueryWrapper<T> query) {
        if (!req.useCustomSort() || !appendCustomSortExpr(query, req.getOrderFields()))
            appendDefaultSortExpr(req, query);
    }

    private boolean appendCustomSortExpr(
            LambdaQueryWrapper<T> query, Collection<QueryOrderByDTO> fields) {
        if (CollectionUtils.isEmpty(fields))
            return false;
        boolean customSortUsed = false;
        for (QueryOrderByDTO reqField : fields) {
            SortField sortField = SortField.findByCode(reqField.getOrderField());
            if (sortField == null) continue;
            SFunction<T, ?> customRecordField = getCustomSortField(sortField);
            if (customRecordField == null) continue;
            customSortUsed = true;
            boolean isAsc = reqField.getIsAsc() != null && reqField.getIsAsc();
            if (isAsc)
                query.orderByAsc(customRecordField);
            else
                query.orderByDesc(customRecordField);
        }
        return customSortUsed;
    }

    /**
     * 默认排序
     */
    abstract void appendDefaultSortExpr(
            PendingMessagePageRequest req,
            LambdaQueryWrapper<T> query);

    /**
     * 获取请求中传的排序字段
     */
    abstract SFunction<T, ?> getCustomSortField(SortField field);

    /**
     * 排序字段
     */
    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    protected enum SortField {

        CREATE_AT("createTimestamp"),
        UPDATE_AT("updateTimestamp"),
        ID("id");

        final String code;

        static SortField findByCode(String code) {
            return Stream.of(SortField.values())
                    .filter(f -> f.code.equals(code))
                    .findFirst()
                    .orElse(null);
        }

    }
}