package com.jy.framework.mybatis.join;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

import java.util.Date;
import java.util.List;

@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public class SqlWrapper<T> extends AbstractQueryColumn {

    private static final JoinMapper mapper = SpringUtil.getBean(JoinMapper.class);

    protected final Sql<T> sql;

    /**
     * 将查询条件转换为临时表
     */
    public TempTable toTable(String alias) {
        Assert.notBlank(alias, "temp table alias must not be blank");
        return new TempTable(alias, sql);
    }

    /**
     * where条件
     */
    public SqlWrapper<T> where(AbstractSql... sqlFormats) {
        if (ObjectUtil.isNotEmpty(sqlFormats)) {
            sql.whereList.addAll(ListUtil.toList(sqlFormats));
        }
        return this;
    }

    /**
     * 分组
     */
    public SqlWrapper<T> groupBy(AbstractSql... QueryColumns) {
        if (ObjectUtil.isNotEmpty(QueryColumns)) {
            sql.groupByList.addAll(ListUtil.toList(QueryColumns));
        }
        return this;
    }

    /**
     * 排序
     */
    public SqlWrapper<T> orderBy(OrderBy... queryColumnOrders) {
        if (ObjectUtil.isNotEmpty(queryColumnOrders)) {
            sql.orderByList.addAll(ListUtil.toList(queryColumnOrders));
        }
        return this;
    }

    /**
     * 分页
     */
    public SqlWrapper<T> limit(Number offset) {
        sql.limit = SqlConstant.LIMIT + offset.longValue();
        return this;
    }

    /**
     * 分页
     */
    public SqlWrapper<T> limit(Number offset, Number rows) {
        sql.limit = SqlConstant.LIMIT + offset.longValue() + StringPool.COMMA + rows.longValue();
        return this;
    }

    /**
     * union
     */
    public Sql<T> union() {
        Sql<T> newSql = Sql.create(this.sql.resultType);
        newSql.sqlList.addAll(this.sql.sqlList);
        newSql.sqlList.add(this.sql);

        this.sql.unionType = SqlConstant.UNION;
        this.sql.sqlList.clear();
        return newSql;
    }

    /**
     * union all
     */
    public Sql<T> unionAll() {
        Sql<T> newSql = Sql.create(this.sql.resultType);
        newSql.sqlList.addAll(this.sql.sqlList);
        newSql.sqlList.add(this.sql);

        this.sql.unionType = SqlConstant.UNION_ALL;
        this.sql.sqlList.clear();
        return newSql;
    }

    /**
     * 查询列表
     */
    public List<T> list() {
        List<?> result;
        if (isBaseType(this.sql.resultType)) {
            result = mapper.listObj(sql);
        } else {
            result = mapper.listMap(sql);
        }

        return BeanUtil.copyToList(result, this.sql.resultType);
    }

    /**
     * 查询分页
     */
    public Page<T> page(Page<?> page) {
        if (isBaseType(this.sql.resultType)) {
            mapper.pageObj(sql, page);
        } else {
            mapper.pageMap(sql, page);
        }

        Page<T> result = new Page<>();
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());
        result.setTotal(page.getTotal());
        result.setRecords(BeanUtil.copyToList(page.getRecords(), this.sql.resultType));
        return result;
    }

    /**
     * 查询单条
     */
    public T one() {
        Object result;
        if (isBaseType(this.sql.resultType)) {
            result = mapper.oneObj(sql);
        } else {
            result = mapper.oneMap(sql);
        }

        return Convert.convert(this.sql.resultType, result);
    }

    @Override
    protected AbstractQueryColumn copy() {
        return new SqlWrapper<>(sql);
    }

    @Override
    protected String format() {
        return spliceCompute(StringPool.LEFT_BRACKET + sql.format() + StringPool.RIGHT_BRACKET);
    }

    private Boolean isBaseType(Class<?> cls) {
        return String.class == cls
                || Number.class.isAssignableFrom(cls)
                || Date.class.isAssignableFrom(cls)
                || Boolean.class.isAssignableFrom(cls)
                || Character.class.isAssignableFrom(cls)
                || cls.isEnum();
    }

}