package org.allaymc.codegen;

import com.google.gson.JsonParser;
import com.palantir.javapoet.*;
import lombok.SneakyThrows;
import org.allaymc.dependence.StringUtils;

import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author daoge_cmd | Cool_Loong | IWareQ
 */
public class ItemIdEnumGen {
    public static final Map<String, Integer> ITEM_NAME_TO_ID = new TreeMap<>();
    public static final Path ITEM_DATA_FILE_PATH = Path.of(CodeGenConstants.DATA_PATH + "items.json");
    private static final String PACKAGE_NAME = "org.allaymc.server.item.data";

    static {
        try {
            var reader = new InputStreamReader(Files.newInputStream(ITEM_DATA_FILE_PATH));
            JsonParser.parseReader(reader).getAsJsonObject().entrySet().forEach(entry -> {
                ITEM_NAME_TO_ID.put(entry.getKey(), entry.getValue().getAsJsonObject().get("id").getAsInt());
            });
        } catch (IOException e) {
            throw new CodeGenException(e);
        }
    }

    public static void main(String[] args) {
        generate();
    }

    @SneakyThrows
    public static void generate() {
        generateToDependenceModule();
        generateToServerModule();
    }

    @SneakyThrows
    public static void generateToDependenceModule() {
        TypeSpec.Builder codeBuilder = commonBuilder(TypeNames.DEP_IDENTIFIER);
        addEnums(codeBuilder);
        var javaFile = JavaFile.builder("org.allaymc.dependence", codeBuilder.build())
                .indent(CodeGenConstants.INDENT)
                .skipJavaLangImports(true)
                .build();
        Utils.writeFileWithCRLF(Path.of("codegen/src/main/java/org/allaymc/dependence/ItemId.java"), javaFile.toString());
    }

    @SneakyThrows
    public static void generateToServerModule() {
        TypeSpec.Builder codeBuilder = commonBuilder(TypeNames.API_IDENTIFIER)
                .addMethod(MethodSpec.methodBuilder("fromIdentifier")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .addParameter(TypeNames.API_IDENTIFIER, "identifier")
                        .beginControlFlow("try")
                        .addStatement("return valueOf(identifier.path().replace(\".\", \"_\").toUpperCase($T.ROOT))", Locale.class)
                        .nextControlFlow("catch ($T ignore)", IllegalArgumentException.class)
                        .addStatement("return null")
                        .endControlFlow()
                        .returns(TypeNames.ITEM_ID)
                        .build()
                )
                .addMethod(MethodSpec.methodBuilder("getItemType")
                        .addModifiers(Modifier.PUBLIC)
                        .addStatement("return $T.ITEMS.get(this.getIdentifier())", TypeNames.REGISTRIES)
                        .returns(ParameterizedTypeName.get(TypeNames.ITEM_TYPE, WildcardTypeName.subtypeOf(ClassName.OBJECT)))
                        .build()
                );
        addEnums(codeBuilder);
        var javaFile = JavaFile.builder(PACKAGE_NAME, codeBuilder.build())
                .indent(CodeGenConstants.INDENT)
                .skipJavaLangImports(true)
                .build();
        Utils.writeFileWithCRLF(Path.of("server/src/main/java/org/allaymc/server/item/data/ItemId.java"), javaFile.toString());
    }

    private static void addEnums(TypeSpec.Builder codeBuilder) {
        for (var entry : ITEM_NAME_TO_ID.entrySet()) {
            var split = StringUtils.fastTwoPartSplit(
                    StringUtils.fastTwoPartSplit(entry.getKey(), ":", "")[1],
                    ".", "");
            var valueName = split[0].isBlank() ? split[1].toUpperCase(Locale.ROOT) : split[0].toUpperCase(Locale.ROOT) + "_" + split[1].toUpperCase(Locale.ROOT);
            codeBuilder.addEnumConstant(valueName, TypeSpec.anonymousClassBuilder("$S, $L", entry.getKey(), entry.getValue()).build());
        }
    }

    private static TypeSpec.Builder commonBuilder(ClassName identifierClass) {
        return TypeSpec.enumBuilder("ItemId")
                .addModifiers(Modifier.PUBLIC)
                .addField(FieldSpec
                        .builder(identifierClass, "identifier", Modifier.PRIVATE, Modifier.FINAL)
                        .addAnnotation(TypeNames.GETTER)
                        .build())
                .addField(FieldSpec
                        .builder(int.class, "runtimeId", Modifier.PRIVATE, Modifier.FINAL)
                        .addAnnotation(TypeNames.GETTER)
                        .build())
                .addMethod(MethodSpec.constructorBuilder()
                        .addParameter(TypeNames.STRING, "identifier")
                        .addParameter(int.class, "runtimeId")
                        .addStatement("this.identifier = new $T(identifier)", identifierClass)
                        .addStatement("this.runtimeId = runtimeId")
                        .build()
                );
    }
}
