package com.ruoyi.common.utils.wrapper;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.util.Collection;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;

@SuppressWarnings("unused")
public class IWrapper<T> extends AbstractWrapper<T, String, IWrapper<T>> implements Query<IWrapper<T>, T, String> {

    // =====================================================================
    // QueryWrapper原生方法
    // =====================================================================

    /**
     * 查询字段
     */
    private final SharedString sqlSelect = new SharedString();

    /**
     * 非对外公开的构造方法,只用于生产嵌套 sql
     *
     * @param entityClass 本不应该需要的
     */
    private IWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,
                     Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
                     SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
        super.setEntity(entity);
        super.setEntityClass(entityClass);
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
    }

    @Override
    public IWrapper<T> select(String... columns) {
        if (ArrayUtils.isNotEmpty(columns)) {
            this.sqlSelect.setStringValue(String.join(StringPool.COMMA, columns));
        }
        return typedThis;
    }

    @Override
    public IWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
        super.setEntityClass(entityClass);
        this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(getEntityClass()).chooseSelect(predicate));
        return typedThis;
    }

    @Override
    public String getSqlSelect() {
        return sqlSelect.getStringValue();
    }

    /**
     * 用于生成嵌套 sql
     * <p>
     * 故 sqlSelect 不向下传递
     * </p>
     */
    @Override
    protected IWrapper<T> instance() {
        return new IWrapper<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs, new MergeSegments(),
                SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
    }

    @Override
    public void clear() {
        super.clear();
        sqlSelect.toNull();
    }

    // =====================================================================
    // 初始化
    // =====================================================================

    private IWrapper() {
        super.initNeed();
    }

    public static <T> IWrapper<T> getWrapper() {
        return new IWrapper<>();
    }

    // =====================================================================
    // 拓展方法
    // =====================================================================

    // /**
    //  * 过滤出有权限查看的角色数据
    //  */
    // public <K, V> IWrapper<T> roleDataScopes(SFunction<K, V> function) {
    //     return roleDataScopes(getSqlFieldName(function));
    // }
    //
    // public <K, V> IWrapper<T> roleDataScopes(String alias, SFunction<K, V> function) {
    //     return roleDataScopes(getSqlFieldName(alias, function));
    // }
    //
    // public <K, V> IWrapper<T> roleDataScopes(String sqlFieldName) {
    //     if (SaTokenUtil.haveAllDataScopes()) return typedThis;
    //     return in(sqlFieldName, SaTokenUtil.getRoleDataScopes());
    // }
    //
    // /**
    //  * 过滤出有权限查看的部门数据
    //  */
    // public <K, V> IWrapper<T> deptDataScopes(SFunction<K, V> function) {
    //     return deptDataScopes(getSqlFieldName(function));
    // }
    //
    // public <K, V> IWrapper<T> deptDataScopes(String alias, SFunction<K, V> function) {
    //     return deptDataScopes(getSqlFieldName(alias, function));
    // }
    //
    // public <K, V> IWrapper<T> deptDataScopes(String sqlFieldName) {
    //     if (SaTokenUtil.haveAllDataScopes()) return typedThis;
    //     return in(sqlFieldName, SaTokenUtil.getDeptDataScopes());
    // }
    //
    // /**
    //  * 过滤出有权限查看的用户数据
    //  */
    // public <K, V> IWrapper<T> userDataScopes(SFunction<K, V> function) {
    //     return userDataScopes(getSqlFieldName(function));
    // }
    //
    // public <K, V> IWrapper<T> userDataScopes(String alias, SFunction<K, V> function) {
    //     return userDataScopes(getSqlFieldName(alias, function));
    // }
    //
    // public <K, V> IWrapper<T> userDataScopes(String sqlFieldName) {
    //     if (SaTokenUtil.haveAllDataScopes()) return typedThis;
    //     return in(sqlFieldName, SaTokenUtil.getUserDataScopes());
    // }

    public IWrapper<T> keyword(Object value, SqlField... keywordData) {
        this.and(ObjectUtil.isNotEmpty(value), q -> {
            for (SqlField keywordDatum : keywordData) {
                q.or().like(keywordDatum.getName(), value);
            }
        });
        return typedThis;
    }

    /**
     * 查询字段
     */
    public final <K, V> IWrapper<T> select(SqlField... sqlFields) {
        StringJoiner stringJoiner = new StringJoiner(StringPool.COMMA);

        for (SqlField sqlField : sqlFields) {
            stringJoiner.add(sqlField.getName());
        }

        this.sqlSelect.setStringValue(stringJoiner.toString());

        return typedThis;
    }

    /**
     * 排序
     */
    public <K, V> IWrapper<T> orderByAsc(SFunction<K, V> function) {
        return orderByAsc(getSqlFieldName(function));
    }

    public <K, V> IWrapper<T> orderByAsc(String alias, SFunction<K, V> function) {
        return orderByAsc(getSqlFieldName(alias, function));
    }

    public <K, V> IWrapper<T> orderByDesc(SFunction<K, V> function) {
        return orderByDesc(getSqlFieldName(function));
    }

    public <K, V> IWrapper<T> orderByDesc(String alias, SFunction<K, V> function) {
        return orderByDesc(getSqlFieldName(alias, function));
    }

    /**
     * between
     */
    public <K, V> IWrapper<T> between(SFunction<K, V> function, Object val1, Object val2) {
        String sqlFieldName = getSqlFieldName(function);
        return between(sqlFieldName, val1, val2);
    }

    public <K, V> IWrapper<T> between(String alias, SFunction<K, V> function, Object val1, Object val2) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return between(sqlFieldName, val1, val2);
    }

    public <K, V> IWrapper<T> between(String sqlFieldName, Object val1, Object val2) {
        return between(ObjectUtil.isAllNotEmpty(val1, val2), sqlFieldName, val1, val2);
    }

    /**
     * eq
     */
    public <K, V> IWrapper<T> eq(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return eq(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> eq(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return eq(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> eq(String sqlFieldName, Object value) {
        return eq(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * ne
     */
    public <K, V> IWrapper<T> ne(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return ne(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> ne(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return ne(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> ne(String sqlFieldName, Object value) {
        return ne(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * ge
     */
    public <K, V> IWrapper<T> ge(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return ge(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> ge(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return ge(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> ge(String sqlFieldName, Object value) {
        return ge(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * gt
     */
    public <K, V> IWrapper<T> gt(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return gt(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> gt(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return gt(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> gt(String sqlFieldName, Object value) {
        return gt(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * le
     */
    public <K, V> IWrapper<T> le(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return le(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> le(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return le(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> le(String sqlFieldName, Object value) {
        return le(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * lt
     */
    public <K, V> IWrapper<T> lt(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return lt(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> lt(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return lt(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> lt(String sqlFieldName, Object value) {
        return lt(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * like
     */
    public <K, V> IWrapper<T> like(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return like(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> like(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return like(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> like(String sqlFieldName, Object value) {
        return apply(ObjectUtil.isNotEmpty(value), "instr(" + sqlFieldName + ", {0}) > 0", value);
    }

    /**
     * notLike
     */
    public <K, V> IWrapper<T> notLike(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return notLike(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> notLike(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return notLike(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> notLike(String sqlFieldName, Object value) {
        return notLike(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * likeLeft
     */
    public <K, V> IWrapper<T> likeLeft(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return likeLeft(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> likeLeft(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return likeLeft(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> likeLeft(String sqlFieldName, Object value) {
        return likeLeft(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * likeRight
     */
    public <K, V> IWrapper<T> likeRight(SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(function);
        return likeRight(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> likeRight(String alias, SFunction<K, V> function, Object value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return likeRight(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> likeRight(String sqlFieldName, Object value) {
        return likeRight(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    /**
     * in
     */
    public <K, V> IWrapper<T> in(SFunction<K, V> function, Collection<?> value) {
        String sqlFieldName = getSqlFieldName(function);
        return in(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> in(String alias, SFunction<K, V> function, Collection<?> value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return in(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> in(String sqlFieldName, Collection<?> value) {
        return in(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    public <K, V> IWrapper<T> in(SFunction<K, V> function, Object... value) {
        String sqlFieldName = getSqlFieldName(function);
        return in(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> in(String alias, SFunction<K, V> function, Object... value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return in(sqlFieldName, value);
    }

    /**
     * notIn
     */
    public <K, V> IWrapper<T> notIn(SFunction<K, V> function, Collection<?> value) {
        String sqlFieldName = getSqlFieldName(function);
        return notIn(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> notIn(String alias, SFunction<K, V> function, Collection<?> value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return notIn(sqlFieldName, value);
    }

    @Override
    public IWrapper<T> notIn(String sqlFieldName, Collection<?> value) {
        return notIn(ObjectUtil.isNotEmpty(value), sqlFieldName, value);
    }

    public <K, V> IWrapper<T> notIn(SFunction<K, V> function, Object... value) {
        String sqlFieldName = getSqlFieldName(function);
        return notIn(sqlFieldName, value);
    }

    public <K, V> IWrapper<T> notIn(String alias, SFunction<K, V> function, Object... value) {
        String sqlFieldName = getSqlFieldName(alias, function);
        return notIn(sqlFieldName, value);
    }

    // =====================================================================
    // 静态方法
    // =====================================================================

    /**
     * 获取对象字段名
     * 例：getFieldName(Entity::getBaseId) -》 return "baseId"
     */
    private static <K, V> String getFieldName(SFunction<K, V> function) {
        LambdaMeta meta = LambdaUtils.extract(function);
        return PropertyNamer.methodToProperty(meta.getImplMethodName());
    }

    /**
     * 获取sql字段名
     * 例：getSqlFieldName(Entity::getBaseId) -》 return "base_id"
     */
    protected static <K, V> String getSqlFieldName(SFunction<K, V> function) {
        return StrUtil.toUnderlineCase(getFieldName(function));
    }

    /**
     * 获取sql字段名
     * 例：getSqlFieldName("s", Entity::getBaseId) -》 return "s.base_id"
     */
    protected static <K, V> String getSqlFieldName(String alias, SFunction<K, V> function) {
        return StrUtil.toUnderlineCase(alias + "." + getFieldName(function));
    }

}
