package xyz.tront.trontmod;

import net.minecraft.SharedConstants;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentInitializers;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.resources.Identifier;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ItemAttributeModifiers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Headless checks. fabric-loader-junit boots Fabric Loader in dev mode;
 * Bootstrap.bootStrap() fills the vanilla registries; then we register ours.
 * Run with: gradlew test
 */
class ModItemsTest {
	/** Player base attack damage; item modifiers add to this. */
	private static final double PLAYER_BASE_ATTACK = 1.0;

	@BeforeAll
	static void bootGame() {
		SharedConstants.tryDetectVersion();
		Bootstrap.bootStrap();
		ModItems.initialize();
		// Since 26.x an item's data components (durability, attributes, ...) are not bound at bootstrap.
		// The server builds them from the full registry lookup when a world loads
		// (ReloadableServerResources -> DATA_COMPONENT_INITIALIZERS.build(...)). Without this, new ItemStack(...)
		// throws "Components not bound yet". VanillaRegistries.createLookup() is the headless equivalent.
		HolderLookup.Provider lookup = VanillaRegistries.createLookup();
		BuiltInRegistries.DATA_COMPONENT_INITIALIZERS.build(lookup)
			.forEach(DataComponentInitializers.PendingComponents::apply);
	}

	private static void assertRegistered(String path, net.minecraft.world.item.Item item) {
		Identifier id = TrontMod.id(path);
		assertTrue(BuiltInRegistries.ITEM.containsKey(id), id + " missing from item registry");
		assertNotNull(item);
		assertEquals(id, BuiltInRegistries.ITEM.getKey(item));
	}

	private static double attackDamage(ItemStack stack) {
		ItemAttributeModifiers mods = stack.get(DataComponents.ATTRIBUTE_MODIFIERS);
		assertNotNull(mods, stack + " has no attribute modifiers");
		return mods.compute(Attributes.ATTACK_DAMAGE, PLAYER_BASE_ATTACK, EquipmentSlot.MAINHAND);
	}

	@Test
	void trontCoinIsRegisteredUnderOurId() {
		assertRegistered("tront_coin", ModItems.TRONT_COIN);
	}

	@Test
	void emeraldSwordIsRegisteredUnderOurId() {
		assertRegistered("emerald_sword", ModItems.EMERALD_SWORD);
	}

	@Test
	void emeraldSwordHitsLikeDiamondButWearsFaster() {
		ItemStack emerald = new ItemStack(ModItems.EMERALD_SWORD);
		ItemStack diamond = new ItemStack(Items.DIAMOND_SWORD);

		assertTrue(emerald.has(DataComponents.WEAPON), "sword() should attach the WEAPON component");
		assertEquals(7.0, attackDamage(emerald), 1e-6);
		assertEquals(attackDamage(diamond), attackDamage(emerald), 1e-6);

		assertEquals(1200, emerald.getMaxDamage());
		assertTrue(emerald.getMaxDamage() < diamond.getMaxDamage());
	}

	@Test
	void emeraldMaterialRepairsWithOurTag() {
		assertEquals(ModItems.EMERALD_TOOL_MATERIALS, ModItems.EMERALD_MATERIAL.repairItems());
		assertEquals(20, ModItems.EMERALD_MATERIAL.enchantmentValue());
	}
}
