package cn.reghao.tnb.sb.db.autobatis.mapper;

import cn.reghao.tnb.sb.db.autobatis.mapper.crud.DeleteGenerator;
import cn.reghao.tnb.sb.db.autobatis.mapper.crud.InsertGenerator;
import cn.reghao.tnb.sb.db.autobatis.mapper.crud.SelectGenerator;
import cn.reghao.tnb.sb.db.autobatis.mapper.crud.UpdateGenerator;
import cn.reghao.tnb.sb.db.autobatis.mapper.element.BaseElement;
import cn.reghao.tnb.sb.db.autobatis.mapper.element.ElementName;
import cn.reghao.tnb.sb.db.autobatis.mapper.element.InsertElement;
import cn.reghao.tnb.sb.db.autobatis.mapper.element.MyBatisMapper;
import cn.reghao.tnb.sb.db.autobatis.StringUtil;
import cn.reghao.tnb.sb.util.ClassUtil;
import cn.reghao.tnb.sb.util.RuntimeClass;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 1.扫描 mapper 接口
 * 2.解析 mapper 方法
 * 3.根据方法名字生成相应的内容
 * 4.添加内容并生成 mapper xml 文件
 * TODO 5.在 MyBatis 配置文件中注册 mapper 接口
 *
 * @author reghao
 * @date 2019-12-06 11:29:14
 */
public class MapperManager {
    // mybatis 配置文件
    private final String mybatisConfig = "";
    private RuntimeClass runtimeClass = new RuntimeClass();
    private InsertGenerator insertGenerator = new InsertGenerator();
    private UpdateGenerator updateGenerator = new UpdateGenerator();
    private DeleteGenerator deleteGenerator = new DeleteGenerator();
    private SelectGenerator selectGenerator = new SelectGenerator();

    /**
     * @param mapperPkg mapper 接口所在包
     * @param mapperDir mapper xml 文件所在目录
     * @return
     * @date 2021-08-11 下午6:43
     */
    public void generateMapperFile(String mapperPkg, String mapperDir) {
        List<Class> classList = runtimeClass.classesInPackage(mapperPkg);
        classList.forEach(clazz -> {
            try {
                String mapper = ClassUtil.classname(clazz);
                MyBatisMapper myBatisMapper = scanMapper(clazz);

                // 生成 mapper 文件
                String xmlPath = String.format("%s/%s.xml", mapperDir, mapper);
                XmlGenerator.createMapperXml(myBatisMapper, new File(xmlPath));
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 通过 mapper 接口生成 mapper 文件，并将 mapper 注册到 mybatis-config.xml
     *
     * @param clazz mapper 接口
     * @date 2020-04-03 下午2:58
     */
    @Deprecated
    public void mapperProcessor(Class clazz) {
        String mapper = ClassUtil.classname(clazz);
        try {
            MyBatisMapper myBatisMapper = scanMapper(clazz);
            String xmlPath = "mapperDir" + "/" + mapper + ".xml";
            XmlGenerator.createMapperXml(myBatisMapper, new File(xmlPath));

            /*File configXml = new File(mybatisConfig);
            String value = xmlPath.split("/src/main/resources/")[1];
            registerMapper(configXml, value);*/
        } catch (Exception e) {
            System.out.println("处理 mapper 接口时发生异常");
            e.printStackTrace();
        }
    }

    /**
     * 扫描 mapper 接口中的 insert 方法
     *
     * @param
     * @return
     * @date 2020-04-07 上午10:34
     */
    private MyBatisMapper scanMapper(Class clazz) throws Exception {
        if (!clazz.isInterface()) {
            System.out.println(clazz.getName() + " 不是一个接口，忽略");
        }

        Method[] methods = clazz.getDeclaredMethods();
        List<BaseElement> elements = new ArrayList<>();
        // 将 mapper 接口中的方法解析为一个 MapperElement 对象
        for (Method method : methods) {
            String methodName = method.getName();
            if (methodName.startsWith("insert")) {
                Parameter[] params = method.getParameters();
                if (params.length != 1) {
                    throw new Exception("insert 方法的参数只能是一个...");
                }

                // 方法参数
                Class paramClass;
                Type type = method.getParameters()[0].getParameterizedType();
                if (type instanceof ParameterizedType) {
                    // 集合中的参数化类型
                    ParameterizedType pt = (ParameterizedType) type;
                    paramClass = (Class) pt.getActualTypeArguments()[0];
                } else {
                    paramClass = (Class) type;
                }

                String className = ClassUtil.classname(paramClass);
                String tableName = StringUtil.separateByUnderline(className);
                Map<String, String> map = ClassUtil.fieldAndTableName(paramClass);

                Map<String, String> attributes = new LinkedHashMap<>();
                attributes.put("id", methodName);
                attributes.put("useGeneratedKeys", "true");
                attributes.put("keyProperty", "id");

                InsertElement insertElement = new InsertElement();
                if (methodName.toLowerCase(Locale.ROOT).contains("batch")) {
                    InsertElement foreachElement = new InsertElement();
                    foreachElement.setName(ElementName.foreach);
                    Map<String, String> childAttributes = new LinkedHashMap<>();
                    // TODO 指定 list 或 set
                    childAttributes.put("collection", "list");
                    childAttributes.put("item", "item");
                    childAttributes.put("index", "index");
                    childAttributes.put("separator", ",");
                    foreachElement.setAttributes(childAttributes);
                    insertElement.setForeachElement(foreachElement);
                    //continue;
                }

                insertElement.setName(ElementName.insert);
                insertElement.setAttributes(attributes);
                insertElement.setTableName(tableName);
                insertElement.setClassFields(new ArrayList<>(map.keySet()));
                insertElement.setTableFields(new ArrayList<>(map.values()));
                insertGenerator.setElementContent(insertElement);
                elements.add(insertElement);
            }
        }

        return new MyBatisMapper(clazz.getName(), elements);
    }

    private void registerMapper(File configXml, String value) throws Exception {
        String parent = "mappers";
        String element = "mapper";
        String attribute = "resource";
        XmlGenerator.registerMapper(configXml, parent, element, attribute, value);
    }
}
