package com.ruoyi.common.dev;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class SQLQueryFuncGen
{
    private String workPath;
    private String queryId;
    private String entity;
    private String module;
    private String packageName;
    private String comment;
    private Map<String, String> parameterType;
    private String returnType;
    private String mapper_java;
    private String service_java;
    private String serviceImpl_java;
    private final static String PARAM_DEFAULT_NAME = "cond";

    public String GetParameterType()
    {
        return GenParameterStr(1);
    }

    public void SetParameterType(String type)
    {
        parameterType = ParseParameterType(type);
    }

    public String GetReturnType()
    {
        return returnType;
    }

    public void SetReturnType(String type)
    {
        this.returnType = ParseReturnType(type);
    }

    public SQLQueryFuncGen()
    {
        workPath = System.getProperty("user.dir").replaceAll(File.pathSeparator, "/");
    }

    private String In(String label)
    {
        Konsole.out.print(Konsole.FC_BLUE, label);
        Scanner scanner = new Scanner(System.in);
        String str = scanner.nextLine();
        if(StrUtil.isEmpty(str))
            return "";
        return str.trim();
    }

    private String WaitIn(String label)
    {
        String str;
        do
        {
            str = In(label);
        }
        while(StrUtil.isEmpty(str));
        return str;
    }

    private String ParseReturnType(String type)
    {
        if(StrUtil.isEmpty(type))
            return "void" ; // entity;
        if(type.equalsIgnoreCase("List"))
            return String.format("List<%s>", entity);
        if(type.equalsIgnoreCase("Map"))
            return "Map<String, Object>";
        if(type.equalsIgnoreCase("List<Map>"))
            return "List<Map<String, Object>>";
        return type;
    }

    private String ReadReturnType(String label)
    {
        String type = In(label);
        return ParseReturnType(type);

    }

    private void GuessType(String type, Map<String, String> res)
    {
        String name = null;
        String newType = null;
        if(type.equalsIgnoreCase("List"))
        {
            name = StrUtil.lowerFirst(entity) + "List";
            newType = String.format("List<%s>", entity);
        }
        else if(type.equalsIgnoreCase("Map"))
        {
            name = "map";
            newType = "Map<String, Object>";
        }
        else if(type.equalsIgnoreCase("ID"))
        {
            name = "id";
            newType = "Long";
        }
        else if(type.equalsIgnoreCase("Long"))
        {
            name = "num";
            newType = "Long";
        }
        else if(type.equalsIgnoreCase("int"))
        {
            name = "i";
            newType = "int";
        }
        else if(type.equalsIgnoreCase("BigDecimal"))
        {
            name = "num";
            newType = "BigDecimal";
        }
        else if(type.equalsIgnoreCase("String"))
        {
            name = "name";
            newType = "String";
        }
        else if(type.equalsIgnoreCase("List<Map>"))
        {
            name = "mapList";
            newType = "List<Map<String, Object>>";
        }
        else if(type.equalsIgnoreCase("void"))
        {
            name = "";
            newType = "";
        }
        else
        {
            if(!type.contains(" "))
            {
                name = StrUtil.lowerFirst(type);
                newType = type;
            }
            else
            {
                String[] arr = type.split(" ");
                for(String s : arr)
                {
                    GuessType(s.trim(), res);
                }
            }
        }
        if(StrUtil.isNotEmpty(name))
        {
            int i = 0;
            String newName = name;
            while(res.containsKey(newName))
                newName = name + (++i);
            res.put(newName, newType);
        }
    }

    private Map<String, String> ReadParameterType(String label)
    {
        String type = In(label);
        return ParseParameterType(type);
    }

    private Map<String, String> ParseParameterType(String type)
    {
        Map<String, String> res = new LinkedHashMap<>();
        if(StrUtil.isEmpty(type))
        {
            /*res.put(PARAM_DEFAULT_NAME, entity);
            return res;*/
            return null;
        }
        GuessType(type, res);
        return res;
    }

    private String GenParameterStr(int type)
    {
        if(null == parameterType)
            return "";
        List<String> list = new ArrayList<>();
        parameterType.forEach((k, v) -> {
            String str = "";
            if((type & 1) != 0)
                str += v;
            if((type & 2) != 0)
                str += (str.isEmpty() ? k : (" " + k));
            list.add(str);
        });
        return CollectionUtil.join(list, ", ");
    }

    public void ScanFilePath()
    {
        String modulePath = String.format("%s/ruoyi-%s/src/main/java/com/ruoyi/%s", workPath, module, StringUtils.isNotEmpty(packageName) ? packageName : module);
        String mapperPath = modulePath + "/" + "mapper";
        String servicePath = modulePath + "/" + "service";
        String serviceImplPath = modulePath + "/" + "service/impl";
        String mapperName = entity + "Mapper";
        String serviceName = "I" + entity + "Service";
        String serviceImplName = entity + "ServiceImpl";
        mapper_java = mapperPath + "/" + mapperName + ".java";
        service_java = servicePath + "/" + serviceName + ".java";
        serviceImpl_java = serviceImplPath + "/" + serviceImplName + ".java";
    }

    private void PrintSummary()
    {
        Konsole.cout(Konsole.FC_GREEN, "Configure: ");
        Konsole.cout(Konsole.FC_CYAN, "Mybatis query ID: " + queryId);
        Konsole.cout(Konsole.FC_CYAN, "Entity class: " + entity);
        Konsole.cout(Konsole.FC_CYAN, "Module name: " + module);
        Konsole.cout(Konsole.FC_CYAN, "Package name: " + packageName);
        Konsole.cout(Konsole.FC_CYAN, "Return type: " + returnType);
        Konsole.cout(Konsole.FC_CYAN, "Parameter type: " + parameterType);
        Konsole.cout(Konsole.FC_CYAN, "Parameter type string: " + GenParameterStr(1 | 2));
        Konsole.cout(Konsole.FC_CYAN, "Mapper file: " + mapper_java);
        Konsole.cout(Konsole.FC_CYAN, "Service type: " + service_java);
        Konsole.cout(Konsole.FC_CYAN, "Service implement type: " + serviceImpl_java);
        Konsole.cout(Konsole.FC_CYAN, "Comment: \n" + GenComment());
        Konsole.cout(Konsole.FC_CYAN, "Function define: \n" + GenFuncDef());
        Konsole.cout(Konsole.FC_CYAN, "Function declare: \n" + GenFuncDecl());
    }

    public boolean IsValid()
    {
        return StrUtil.isNotEmpty(queryId)
                && StrUtil.isNotEmpty(entity)
                && StrUtil.isNotEmpty(module)
                && StrUtil.isNotEmpty(queryId)
                // && StrUtil.isNotEmpty(returnType)
                // && CollectionUtil.isNotEmpty(parameterType)
                ;
    }

    private boolean CheckFile()
    {
        File file = new File(mapper_java);
        if(!file.isFile()) return false;
        file = new File(service_java);
        if(!file.isFile()) return false;
        file = new File(serviceImpl_java);
        if(!file.isFile()) return false;
        return true;
    }

    private String GenFuncDef()
    {
        String str = String.format("\tpublic %s %s(%s);", returnType, queryId, GenParameterStr(1 | 2));
        return str;
    }

    private String GenFuncDecl()
    {
        String mapperProp = StrUtil.lowerFirst(entity) + "Mapper";

        String str = String.format("\t@Override\n\tpublic %s %s(%s)\n\t{\n\t\treturn %s.%s(%s);\n\t}"
                , returnType, queryId, GenParameterStr(1 | 2), mapperProp, queryId, GenParameterStr(2));
        return str;
    }

    public String GenMapperCode()
    {
        StringBuilder sb = new StringBuilder();
        String commentStr = GenComment();
        if(StrUtil.isNotEmpty(commentStr))
            commentStr += "\n";
        String funcDef = commentStr + GenFuncDef();
        sb.append(funcDef);
        return sb.toString();
    }

    public String GenServiceCode()
    {
        return GenMapperCode();
    }

    public String GenServiceImplCode()
    {
        StringBuilder sb = new StringBuilder();
        String commentStr = GenComment();
        if(StrUtil.isNotEmpty(commentStr))
            commentStr += "\n";
        String funcDecl = commentStr + GenFuncDecl();
        sb.append(funcDecl);
        return sb.toString();
    }

    private boolean AppendFile(String content, String path)
    {
        File file = new File(path);
        String source = FileUtil.readUtf8String(file);
        StringBuilder sb = new StringBuilder(source);
        int index = sb.lastIndexOf("}");
        if(index == -1)
            return false;
        sb.insert(index, "\n" + content + "\n");
        String newSource = sb.toString();
        FileUtil.writeUtf8String(newSource, file);
        return true;
    }

    public String GenComment()
    {
        if(StrUtil.isEmpty(comment))
            return "";
        StringBuilder sb = new StringBuilder();
        sb.append("\t/**\n")
                .append("\t * ").append(comment).append("\n")
                .append("\t *\n");
        if(null != parameterType)
        {
            parameterType.forEach((k, v) -> {
                sb.append("\t * @param ").append(k).append(" ").append(v).append("\n");
            });
        }
        sb.append("\t * @return 结果 ").append(returnType).append("\n")
                .append("\t */");

        return sb.toString();
    }

    public boolean Write()
    {
        Konsole.cout(Konsole.FC_GREEN, "Start......");
        if(!CheckFile())
        {
            Konsole.cerr(Konsole.BC_YELLOW | Konsole.S_BG_ENABLED, "File not exists!");
            return false;
        }
        Konsole.cout(Konsole.FC_GREEN, String.format("Handle mapper(%s)......", mapper_java));
        AppendFile(GenMapperCode(), mapper_java);
        Konsole.cout(Konsole.FC_GREEN, String.format("Handle service(%s)......", service_java));
        AppendFile(GenServiceCode(), service_java);
        Konsole.cout(Konsole.FC_GREEN, String.format("Handle serviceImpl(%s)......", serviceImpl_java));
        AppendFile(GenServiceImplCode(), serviceImpl_java);
        Konsole.cout(Konsole.FC_GREEN, "Done!");
        return true;
    }

    public void Reset()
    {
        queryId = "";
        entity = "";
        module = "";
        packageName = "";
        comment = "";
        parameterType = null;
        returnType = "";
        mapper_java = "";
        service_java = "";
        serviceImpl_java = "";
    }

    private boolean Run()
    {
        queryId = WaitIn("Please input Mybatis query id: ");
        entity = WaitIn("Please input entity class: ");
        module = WaitIn("Please input module: ");
        packageName = WaitIn("Please input package name(If empty, using `module`): ");
        returnType = ReadReturnType("Please input return type: ");
        parameterType = ReadParameterType("Please input parameter type: ");
        comment = In("Please input comment: ");
        ScanFilePath();
        PrintSummary();
        return Write();
    }

    public static void main(String[] args)
    {
        SQLQueryFuncGenGUI frame = new SQLQueryFuncGenGUI();
        frame.setVisible(true);

        /*SQLQueryFuncGen gen = new SQLQueryFuncGen();
        gen.Run();*/
    }

    public String getQueryId()
    {
        return queryId;
    }

    public String getEntity()
    {
        return entity;
    }

    public String getModule()
    {
        return module;
    }

    public String getPackageName()
    {
        return packageName;
    }

    public String getComment()
    {
        return comment;
    }

    public String getMapper_java()
    {
        return mapper_java;
    }

    public String getService_java()
    {
        return service_java;
    }

    public String getServiceImpl_java()
    {
        return serviceImpl_java;
    }

    public void setQueryId(String queryId)
    {
        this.queryId = queryId;
    }

    public void setEntity(String entity)
    {
        this.entity = entity;
    }

    public void setModule(String module)
    {
        this.module = module;
    }

    public void setPackageName(String packageName)
    {
        this.packageName = packageName;
    }

    public void setComment(String comment)
    {
        this.comment = comment;
    }

    public void setParameterType(Map<String, String> parameterType)
    {
        this.parameterType = parameterType;
    }

    public void setReturnType(String returnType)
    {
        this.returnType = returnType;
    }
}
