package cn.axzo.workflow.support.api;

import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.printer.DefaultPrettyPrinter;
import com.github.javaparser.printer.Printer;
import com.github.javaparser.utils.SourceRoot;

import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;

/**
 * 生成 WorkflowCoreService 接口
 *
 * @author wangli
 * @since 2024/6/7 17:14
 */
public class CoreServiceCodeGeneration {

    public static void generate() throws Exception {
        String newClassName = "WorkflowCoreService";
//        CompilationUnit originFile = parseOriginFile(newClassName);
        CompilationUnit testGeneric = genericCode("cn.axzo.workflow.starter.api", newClassName);
        writeToStarter(testGeneric, newClassName);

    }

    private static CompilationUnit parseOriginFile(String newClassName) throws Exception {
        String projectRootDir = System.getProperty("user.dir");
        Path sourceCodeRoot = Paths.get(projectRootDir, "/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/");
        SourceRoot sourceRoot = new SourceRoot(sourceCodeRoot);
        sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
        Printer printer = new DefaultPrettyPrinter();
        sourceRoot.setPrinter(printer::print);
        sourceRoot.tryToParse();

        List<CompilationUnit> compilationUnits = sourceRoot.getCompilationUnits();
        for (CompilationUnit cu : compilationUnits) {
            Optional<String> primaryTypeName = cu.getPrimaryTypeName();
            if (primaryTypeName.isPresent() && primaryTypeName.get().equals(newClassName)) {
                System.out.println("compilationUnit = " + cu);
                return cu;
            }
        }
        return null;
    }

    private static void writeToStarter(CompilationUnit cu, String newClassName) {
        String projectRootDir = System.getProperty("user.dir");
        String genericFilePath = Paths.get(projectRootDir, "/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/", newClassName + ".java").toString();
        try (FileWriter fileWriter = new FileWriter(genericFilePath)) {
            fileWriter.append(cu.toString());
        } catch (IOException e) {
            // nothing to do
        }
    }

    private static CompilationUnit genericCode(String pkg, String newClassName) throws Exception {
        CompilationUnit cu = new CompilationUnit(pkg);
        ClassOrInterfaceDeclaration interfaceDeclaration = setCommon(cu, newClassName);

        addMethods(interfaceDeclaration, cu);
        addDefaultMethods(interfaceDeclaration);
        return cu;
    }

    private static void addDefaultMethods(ClassOrInterfaceDeclaration interfaceDeclaration) {
        MethodDeclaration sync = createDefaultMethod("sync", interfaceDeclaration);
        sync.setJavadocComment("强制使用‘同步’模式调用该方法，请在调用真实方法前调用该方法\r\n" +
            "<pre>\r\n" +
            "  workflowCoreService.sync().createProcessInstance();\r\n" +
            "</pre>");

        MethodDeclaration async = createDefaultMethod("async", interfaceDeclaration);
        sync.setJavadocComment("强制使用‘异步’模式调用该方法，请在调用真实方法前调用该方法\r\n" +
            "<pre>\r\n" +
            "  workflowCoreService.async().createProcessInstance();\r\n" +
            "</pre>");
    }

    private static MethodDeclaration createDefaultMethod(String methodName, ClassOrInterfaceDeclaration interfaceDeclaration) {
        // 将sync方法添加到WorkflowCoreService类中
        MethodDeclaration methodDeclaration = interfaceDeclaration.addMethod(methodName, Modifier.Keyword.DEFAULT);
        methodDeclaration.setType(new ClassOrInterfaceType("WorkflowCoreService"));

        // 创建方法体
        BlockStmt methodBody = new BlockStmt();

        MethodCallExpr setMethodCall = new MethodCallExpr(new NameExpr("ThreadUtil"), "set");
        setMethodCall.addArgument(new NameExpr(methodName.toUpperCase())); // 使用NameExpr来表示枚举常量
        ExpressionStmt setStatement = new ExpressionStmt(setMethodCall);

        methodBody.addStatement(setStatement);
        // 创建return this;的语句
        ReturnStmt returnStmt = new ReturnStmt(new ThisExpr());
        methodBody.addStatement(returnStmt);

        // 将方法体设置到sync方法中
        methodDeclaration.setBody(methodBody);
        return methodDeclaration;
    }

    private static void addMethods(ClassOrInterfaceDeclaration interfaceDeclaration, CompilationUnit cu) throws Exception {
        String projectRootDir = System.getProperty("user.dir");
        Path sourceCodeRoot = Paths.get(projectRootDir, "/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/");
        SourceRoot sourceRoot = new SourceRoot(sourceCodeRoot);
        sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
        Printer printer = new DefaultPrettyPrinter();
        sourceRoot.setPrinter(printer::print);
        sourceRoot.tryToParse();

        List<CompilationUnit> compilationUnits = sourceRoot.getCompilationUnits();
        NodeList<BodyDeclaration<?>> targetMethods = interfaceDeclaration.getMembers();
        for (CompilationUnit apiCU : compilationUnits) {
            if (!apiCU.getPrimaryType().filter(e -> e.getAnnotationByName("Manageable").isPresent()).isPresent()) {
                addImports(apiCU, cu);
                // 类上含有 @Manageable，不进行解析
                String interfaceName = apiCU.getPrimaryTypeName().get();
                List<MethodDeclaration> methods = apiCU.getInterfaceByName(interfaceName).get().findAll(MethodDeclaration.class);
                for (MethodDeclaration method : methods) {
                    if (!method.getAnnotationByName("Manageable").isPresent()) {
                        MethodDeclaration methodDeclaration = method.clone();
                        methodDeclaration.setType(changeReturnType(methodDeclaration.getType()));
                        targetMethods.add(methodDeclaration);
                    }
                }
            }
        }
    }

    private static Type changeReturnType(Type type) {
        if (type instanceof ClassOrInterfaceType && type.asClassOrInterfaceType().getTypeArguments().isPresent()) {
            NodeList<Type> types = type.asClassOrInterfaceType().getTypeArguments().orElse(new NodeList<>());
            if (types.size() != 1) {
                throw new RuntimeException("workflow-engine-api 中的接口返回类型有误");
            }
            return types.getFirst().get();
        }
        return type;
    }

    private static void addImports(CompilationUnit apiCU, CompilationUnit cu) {
        for (ImportDeclaration sourceImport : apiCU.getImports()) {
            cu.addImport(sourceImport.clone());
        }
    }

    private static ClassOrInterfaceDeclaration setCommon(CompilationUnit cu, String newClassName) {
        ClassOrInterfaceDeclaration classOrInterfaceDeclaration = cu.addInterface(newClassName).setPublic(true);
        classOrInterfaceDeclaration.setJavadocComment("Workflow Engine Starter Core Service\r\n<p>\r\n" +
            "该类是根据 API 动态生成，不同版本可能会开放新的接口，或回收一些旧接口\r\n<p>\r\n" +
            "Auto generation by workflow engine, It cannot be manually modified");
        classOrInterfaceDeclaration.addAndGetAnnotation("org.springframework.cloud.openfeign.FeignClient")
            .addPair("name", new StringLiteralExpr("workflow-engine"))
            .addPair("url", new StringLiteralExpr("${axzo.service.workflow-engine:http://workflow-engine:8080}"))
            .addPair("configuration", new ClassExpr(new ClassOrInterfaceType("WorkflowEngineStarterFeignConfiguration")));
        cu.addImport("cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration", false, false);
        cu.addImport("cn.axzo.workflow.common.util.ThreadUtil", false, false);
        cu.addImport("cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC", true, false);
        cu.addImport("cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC", true, false);
        return classOrInterfaceDeclaration;
    }
}
