/*
 * Copyright 2020. the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package group.idealworld.dew.sdkgen.process;

import com.ecfront.dew.common.$;
import com.github.jknack.handlebars.Handlebars;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Collectors;

import static group.idealworld.dew.sdkgen.Constants.*;

/**
 * The type Test generate process.
 *
 * @author gudaoxuri
 */
@Slf4j
public class TestGenerateProcess {

    private static final String TEST_FILE_NAME = "DewSDKGenTest.java";

    /**
     * Process file.
     *
     * @param basePath    the base path
     * @param openAPIPath the open api path
     * @param mainClass   the main class
     */
    @SneakyThrows
    public static void process(File basePath, String openAPIPath, String mainClass) {
        log.info("Test class generation.");
        if (openAPIPath != null && !openAPIPath.isBlank()) {
            log.debug("Parameter [{}={}], skip test class generation.", FLAG_DEW_SDK_GEN_OPENAPI_PATH, openAPIPath);
            writeOpenAPIFile(basePath, openAPIPath);
            return;
        }
        String baseClassPath;
        if (mainClass == null || mainClass.isBlank()) {
            baseClassPath = scanMainClassPath(basePath);
        } else {
            baseClassPath = mainClass.substring(0, mainClass.lastIndexOf("."));
            log.debug("Parameter [{}={}], skip main class scan.", FLAG_DEW_MAIN_CLASS, mainClass);
        }
        File testFile = new File(basePath.getPath() + File.separator +
                "src" + File.separator +
                "test" + File.separator +
                "java" + File.separator +
                baseClassPath.replaceAll("\\.", File.separator.equals("\\") ? "\\" + File.separator : File.separator) + File.separator +
                TEST_FILE_NAME);
        if (testFile.exists()) {
            log.debug("Test file [{}] already exists, skip test file generation.", testFile.getPath());
            return;
        }
        writeTestFile(testFile.getPath(), baseClassPath);
    }

    private static void writeOpenAPIFile(File basePath, String openAPIPath) throws IOException {
        var openAPIJson = $.http.get(openAPIPath);
        var targetPath = basePath.getPath() + File.separator + "target" + File.separator + "dew-sdkgen";
        Files.createDirectories(Paths.get(targetPath));
        Files.write(Paths.get(targetPath + File.separator + GENERATED_OPENAPI_FILE_NAME), openAPIJson.getBytes());
    }

    private static String scanMainClassPath(File basePath) {
        return scanMainClassPath(null, Objects.requireNonNull(new File(basePath.getPath() + File.separator + "src" + File.separator + "main")
                .listFiles((dir, name) ->
                        // TODO 其它语言
                        name.equals("java") || name.equals("scala") || name.equals("groovy")
                )));
    }

    private static String scanMainClassPath(String classPackage, File[] files) {
        for (File file : files) {
            if (file.isDirectory()) {
                return scanMainClassPath(
                        // 为空时表示第一次进入，文件名为java scala groovy 等，要忽略
                        classPackage == null
                                ? "" : classPackage.isBlank()
                                ? file.getName() : classPackage + "." + file.getName(),
                        Objects.requireNonNull(file.listFiles()));
            } else {
                var fileContent = $.file.readAllByFile(file, StandardCharsets.UTF_8);
                if (fileContent.contains("@SpringBootApplication")
                        || fileContent.contains("static void main(String[]")
                        || fileContent.contains("def main(")
                        || fileContent.contains("static void main(")
                ) {
                    return classPackage;
                }
            }
        }
        throw new RuntimeException("No main method found.");
    }

    @SneakyThrows
    private static void writeTestFile(String testFilePath, String baseClassPath) {
        String testFileStr = new BufferedReader(new InputStreamReader(
                TestGenerateProcess.class.getResourceAsStream("/testfile/testFile.mustache")))
                .lines().collect(Collectors.joining("\n"));
        var handlebars = new Handlebars();
        var template = handlebars.compileInline(testFileStr);
        testFileStr = template.apply(new HashMap<>() {
            {
                put("package", baseClassPath);
                put("sdkGenPath", GENERATED_BASE_PATH);
            }
        });
        Files.createDirectories(Paths.get(testFilePath).getParent());
        Files.write(Paths.get(testFilePath), testFileStr.getBytes());
    }

}

@@ -3356,38 +3356,72 @@
 Path) %7B%0A        
-return
+String%5B%5D mainClassPath = %7Bnull%7D;%0A       
  scanMainClassPa
@@ -3722,18 +3722,202 @@
       ))
-);
+, mainClassPath);%0A        if (mainClassPath%5B0%5D == null) %7B%0A            throw new RuntimeException(%22No main method found.%22);%0A        %7D else %7B%0A            return mainClassPath%5B0%5D;%0A        %7D
 %0A    %7D%0A%0A
@@ -3927,38 +3927,36 @@
  private static 
-String
+void
  scanMainClassPa
@@ -3987,24 +3987,53 @@
 File%5B%5D files
+, String%5B%5D foundMainClassPath
 ) %7B%0A        
@@ -4054,24 +4054,111 @@
  : files) %7B%0A
+            if (foundMainClassPath%5B0%5D != null) %7B%0A                return;%0A            %7D%0A
             
@@ -4199,23 +4199,16 @@
         
-return 
 scanMain
@@ -4543,16 +4543,36 @@
 Files())
+, foundMainClassPath
 );%0A     
@@ -4987,22 +4987,39 @@
         
-return
+foundMainClassPath%5B0%5D =
  classPa
@@ -5071,69 +5071,8 @@
   %7D%0A
-        throw new RuntimeException(%22No main method found.%22);%0A
     

Fix error in main method finding.