package com.czg.utils;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.constant.SqlConnector;
import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.query.*;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.LambdaGetter;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.function.Consumer;

import static com.mybatisflex.core.util.LambdaUtil.getFieldName;


/**
 * 关联查询的queryWrapper
 *
 * @author Administrator
 */
public class JoinQueryWrapper extends QueryWrapper {
    public JoinQueryWrapper() {
        super();
    }

    private static SerializedLambda getSerializedLambda(Serializable getter) {
        try {
            Method method = getter.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            return (SerializedLambda) method.invoke(getter);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String getImplClassName(SerializedLambda lambda) {
        String type = lambda.getInstantiatedMethodType();
        return type.substring(2, type.indexOf(";"));
    }

    private static Class<?> getImplClass0(SerializedLambda lambda) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String implClass = getImplClassName(lambda);
        try {
            return Class.forName(implClass.replace("/", "."), true, classLoader);
        } catch (ClassNotFoundException e) {
            throw FlexExceptions.wrap(e);
        }
    }

    public <T> String getColum(LambdaGetter<T> column) {
        SerializedLambda lambda = getSerializedLambda(column);
        Class<?> entityClass = getImplClass0(lambda);
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
        return tableInfo.getTableName() + "." + StrUtil.toUnderlineCase(getFieldName(column));
    }

    public static <T> QueryColumn column(LambdaGetter<T> getter) {
        SerializedLambda lambda = getSerializedLambda(getter);
        Class<?> entityClass = getImplClass0(lambda);
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
        String fieldName = getFieldName(getter);
        QueryColumn columnByProperty = tableInfo.getQueryColumnByProperty(fieldName);
        columnByProperty.setName(tableInfo.getTableName() + "." + StrUtil.toUnderlineCase(fieldName));
        return columnByProperty;
    }

    @Override
    public JoinQueryWrapper and(Consumer<QueryWrapper> consumer, boolean condition) {
        if (!condition) {
            return this;
        }
        JoinQueryWrapper newWrapper = new JoinQueryWrapper();
        consumer.accept(newWrapper);
        QueryCondition whereQueryCondition = newWrapper.whereQueryCondition;
        if (whereQueryCondition != null) {
            and(new Brackets(whereQueryCondition));
        }
        return this;
    }

    @Override
    public JoinQueryWrapper or(Consumer<QueryWrapper> consumer, boolean condition) {
        if (!condition) {
            return this;
        }
        JoinQueryWrapper newWrapper = new JoinQueryWrapper();
        consumer.accept(newWrapper);
        QueryCondition whereQueryCondition = newWrapper.whereQueryCondition;
        if (whereQueryCondition != null) {
            or(new Brackets(whereQueryCondition));
        }
        return this;
    }

    @Override
    public JoinQueryWrapper or(Consumer<QueryWrapper> consumer) {
        return or(consumer, true);
    }

    @Override
    public JoinQueryWrapper and(Consumer<QueryWrapper> consumer) {
        return and(consumer, true);
    }

    @Override
    public QueryWrapper and(QueryCondition queryCondition) {
        return addWhereQueryCondition(queryCondition, SqlConnector.AND);
    }

    @Override
    public <T> JoinQueryWrapper eq(LambdaGetter<T> column, Object value) {
        eq(getColum(column), value);
        return this;
    }

    @Override
    public <T> JoinQueryWrapper lt(LambdaGetter<T> column, Object value) {
        lt(getColum(column), value);
        return this;
    }

    @Override
    public <T> QueryWrapper gt(LambdaGetter<T> column, Object value) {
        gt(getColum(column), value);
        return this;
    }

    @Override
    public <T> QueryWrapper in(LambdaGetter<T> column, Object... values) {
        in(getColum(column), values);
        return this;
    }

    @Override
    public <T> QueryWrapper notIn(LambdaGetter<T> column, Object... values) {
        notIn(getColum(column), values);
        return this;
    }

    @Override
    public <T> JoinQueryWrapper orderBy(LambdaGetter<T> column, Boolean asc) {
        orderBy(getColum(column), asc);
        return this;
    }

    @Override
    public <T> QueryWrapper groupBy(LambdaGetter<T> column) {
        return super.groupBy(getColum(column));
    }

    @Override
    public <T> JoinQueryWrapper like(LambdaGetter<T> column, Object value) {
        like(getColum(column), value);
        return this;
    }


}
