package cn.forevermc.launcher.util;

import cn.forevermc.launcher.bean.Game;
import cn.forevermc.launcher.service.GameService;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.imyeyu.fx.task.RunAsync;
import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.StaticInject;
import com.imyeyu.io.IO;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.network.FileRequest;
import com.imyeyu.network.GsonRequest;
import com.imyeyu.network.TimiRequest;
import com.imyeyu.utils.Digest;
import com.imyeyu.utils.OS;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.fluent.Request;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * Fabric API 解析器
 *
 * @author 夜雨
 * @since 2022-11-21 10:33
 */
@Slf4j
@StaticInject
public class FabricBuilder extends RunAsync<Game> implements OS.FileSystem {

	public static final String API = "https://maven.fabricmc.net/";
	public static final String LOADER_URL = "https://meta.fabricmc.net/v2/versions/loader";
	private static final String API_MIRROR_URL = Resources.TIMI_SERVER_API + "/mirror/%s";
	private static final String ATTACH_DL_URL = Resources.TIMI_SERVER_API + "/attachment/download/%s";

	@Inject
	private static Gson gson;

	@Inject
	private static GameService gameService;

	@Setter
	private Game buildGame;

	@Override
	protected final Game call() throws Exception {
		if (buildGame == null) {
			throw new TimiException(TimiCode.ARG_MISS);
		}
		String oldName = buildGame.getName();
		String newName = oldName + "-Fabric";
		log.info("installing fabric: {}", newName);
		Game.MetaData.RealVersion realVersion = gameService.getRealVersion(buildGame);
		// 原版数据
		File jsonFile = buildGame.getJson();
		JsonObject jsonRoot = buildGame.getJsonRoot();
		// 获取加载器
		log.info("fetching fabric loader..");
		JsonArray loaderList = GsonRequest.get(LOADER_URL).asJsonArray();
		JsonObject loader = null;
		for (int i = 0; i < loaderList.size(); i++) {
			loader = loaderList.get(i).getAsJsonObject();
			if (loader.get("stable").getAsBoolean()) {
				break;
			}
		}
		if (loader == null) {
			log.info("not found support fabric loader, game: {}", oldName);
			throw new TimiException(TimiCode.RESULT_NULL, TimiFXUI.MULTILINGUAL.text("fmc.launcher.pack_option.fabric.not_support_api"));
		}
		// 获取 Fabric Loader 加载器的游戏版本版本依赖
		JsonObject fabricData, fabricMeta;
		{
			String loaderVersion = loader.get("version").getAsString();
			// Fabric Loader 数据
			String fabricMetaResp;
			try {
				fabricMetaResp = Request.get(LOADER_URL + "/" + oldName + "/" + loaderVersion).execute().returnContent().asString();
			} catch (IOException e) {
				log.error("not found support fabric api, game: {}", oldName);
				throw new TimiException(TimiCode.RESULT_BAD, TimiFXUI.MULTILINGUAL.text("fmc.launcher.pack_option.fabric.not_support"));
			}
			log.info("injecting fabric lib to original json");
			fabricData = JsonParser.parseString(fabricMetaResp).getAsJsonObject();
			fabricMeta = fabricData.get("launcherMeta").getAsJsonObject();
		}
		// 改名
		jsonRoot.addProperty("id", newName);
		// 启动类
		jsonRoot.addProperty("mainClass", fabricMeta.get("mainClass").getAsJsonObject().get("client").getAsString());
		{
			// 向原版注入 Fabric Loader 依赖
			JsonArray minecraftLibs = jsonRoot.get("libraries").getAsJsonArray();
			{
				// Fabric Loader 加载器
				JsonObject fabricLoader = fabricData.get("loader").getAsJsonObject();
				fabricLoader.addProperty("url", API);
				fabricLoader.addProperty("name", fabricLoader.get("maven").getAsString());
				minecraftLibs.add(injectToMinecraftLib(fabricLoader));
			}
			{
				// Fabric Loader 中间件
				JsonObject fabricIntermediary = fabricData.get("intermediary").getAsJsonObject();
				fabricIntermediary.addProperty("url", API);
				fabricIntermediary.addProperty("name", fabricIntermediary.get("maven").getAsString());
				minecraftLibs.add(injectToMinecraftLib(fabricIntermediary));
			}
			{
				// Fabric API mod
				List<FabricAPIItem> apiItemList = TimiRequest.<List<FabricAPIItem>>get(API_MIRROR_URL.formatted("FabricAPI")).result();
				FabricAPIItem item = null;
				for (FabricAPIItem fabricAPI : apiItemList) {
					if (fabricAPI.minecraftVer.equals(realVersion.getValue())) {
						item = fabricAPI;
						break;
					}
				}
				if (item == null) {
					throw new TimiException(TimiCode.RESULT_NULL, "not found fabric-api");
				}
				File modFile = IO.file(IO.fitPath(buildGame.getPath().getAbsolutePath()) + "mods" + SEP + item.name);
				FileRequest.get(ATTACH_DL_URL.formatted(item.mongoId)).toFile(modFile);
			}
			{
				// 其他依赖
				JsonArray fabricLibsClient = fabricMeta.get("libraries").getAsJsonObject().get("client").getAsJsonArray();
				JsonArray fabricLibsCommon = fabricMeta.get("libraries").getAsJsonObject().get("common").getAsJsonArray();
				for (int i = 0; i < fabricLibsClient.size(); i++) {
					minecraftLibs.add(injectToMinecraftLib(fabricLibsClient.get(i).getAsJsonObject()));
				}
				for (int i = 0; i < fabricLibsCommon.size(); i++) {
					minecraftLibs.add(injectToMinecraftLib(fabricLibsCommon.get(i).getAsJsonObject()));
				}
			}
		}
		// 原版版本
		jsonRoot.addProperty("source-id", oldName);
		// 加载器会修改标题，此标题用于判定是否启动成功
		jsonRoot.addProperty("title", "Minecraft* " + oldName);
		log.info("rename game file");
		// 写入文件
		IO.toFile(jsonFile, gson.toJson(jsonRoot));
		// 重命名版本
		File minecraftJarFile = buildGame.getJar();
		if (minecraftJarFile != null && minecraftJarFile.exists()) {
			IO.rename(minecraftJarFile, newName + ".jar");
		}
		// 重命名配置
		buildGame.setYaml(IO.rename(buildGame.getYaml(), newName + ".yaml"));
		buildGame.setJson(IO.rename(jsonFile, newName + ".json"));
		IO.rename(jsonFile.getParentFile(), newName);
		// 重载
		return gameService.buildGame(new File(Path.ABS_PACK + newName));
	}

	/**
	 * 将 Fabric 依赖转为原版结构 JSON
	 *
	 * @param fabricLib Fabric 依赖
	 * @return 原版结构 JSON
	 */
	private JsonObject injectToMinecraftLib(JsonObject fabricLib) throws Exception {
		JsonObject minecraftLib = new JsonObject();

		// net.fabricmc:tiny-mappings-parser:0.3.0+build.17
		String name = fabricLib.get("name").getAsString();
		String[] names = name.split(":");

		StringBuilder pathSB = new StringBuilder();
		String[] paths = names[0].split("\\.");
		for (int i = 0; i < paths.length; i++) {
			pathSB.append(paths[i]).append('/');
		}
		pathSB.append(names[1]).append('/').append(names[2]).append('/').append(names[1]).append('-').append(names[2]).append(".jar");

		minecraftLib.addProperty("name", name);
		JsonObject downloads = new JsonObject();
		{
			String url = fabricLib.get("url").getAsString() + pathSB;

			JsonObject artifact = new JsonObject();
			artifact.addProperty("path", pathSB.toString());
			artifact.addProperty("url", url);
			// TODO 耗时操作，告知 UI
			if (fabricLib.has("size")) {
				artifact.addProperty("size", fabricLib.get("size").getAsLong());
			} else {
				artifact.addProperty("size", FileRequest.get(url).length());
			}
			if (fabricLib.has("sha1")) {

				artifact.addProperty("sha1", fabricLib.get("sha1").getAsString());
			} else {
				artifact.addProperty("sha1", Digest.sha1(FileRequest.get(url).asBytes()));
			}
			downloads.add("artifact", artifact);
			minecraftLib.add("downloads", downloads);
		}
		return minecraftLib;
	}

	/**
	 * Fabric API mod 对象
	 *
	 * @author 夜雨
	 * @since 2024-07-04 19:48
	 */
	private static class FabricAPIItem {

		/** 文件名 */
		String name;

		/** Fabric 版本 */
		String fabricVer;

		/** Minecraft 版本 */
		String minecraftVer;

		/** 下载 MongoId */
		String mongoId;
	}
}
