package ru.dbotthepony.mc.otm.core

import net.minecraft.ChatFormatting
import net.minecraft.core.HolderLookup
import net.minecraft.core.component.DataComponents
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.Item.TooltipContext
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.component.CustomData
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.batteryLevel
import ru.dbotthepony.mc.otm.capability.fluidLevel
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapPresent
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.runIfClient

class TooltipList {
	fun interface TooltipProvider {
		fun invoke(itemStack: ItemStack, context: TooltipContext, acceptor: (text: Component) -> Unit)
	}

	private val descriptionLines = ArrayList<TooltipProvider>()

	fun assemble(itemStack: ItemStack, context: TooltipContext, into: (Component) -> Unit) {
		if (descriptionLines.isNotEmpty()) {
			if (ClientConfig.HIDE_DESCRIPTION && !minecraft.window.isShiftDown && descriptionLines.any { var flag = false; it.invoke(itemStack, context) { flag = true }; flag }) {
				into.invoke(TranslatableComponent("otm.gui.shift_for_more_info").withStyle(ChatFormatting.GRAY).withStyle(ChatFormatting.ITALIC))
			} else {
				for (lines in descriptionLines) {
					lines.invoke(itemStack, context, into)
				}
			}
		}
	}

	fun assemble(itemStack: ItemStack, context: TooltipContext, into: MutableCollection<Component>) {
		assemble(itemStack, context, into::add)
	}

	fun addNormal(function: TooltipProvider) {
		descriptionLines.add(function)
	}

	@JvmName("addPlain")
	fun add(component: Component) {
		descriptionLines.add { _, _, result -> result.invoke(component.copy()) }
	}

	@JvmName("addFunction2Component")
	fun add(component: () -> Component) {
		descriptionLines.add { _, _, result -> result.invoke(component.invoke()) }
	}

	@JvmName("addDetailed")
	fun addDetailed(component: Component) {
		descriptionLines.add { _, _, result -> if (runIfClient(true) { minecraft.window.isShiftDown }) result.invoke(component.copy()) }
	}

	@JvmName("addDetailedFunction2Component")
	fun addDetailed(component: () -> Component) {
		descriptionLines.add { _, _, result -> if (runIfClient(true) { minecraft.window.isShiftDown }) result.invoke(component.invoke()) }
	}

	fun needsNoPower(formatting: ChatFormatting = ChatFormatting.GRAY) {
		add(TranslatableComponent("otm.needs_no_power").withStyle(formatting))
	}

	fun painted(color: DyeColor?) {
		if (color != null) {
			add(TranslatableComponent("otm.painted.${color.name.lowercase()}").withStyle(ChatFormatting.DARK_GRAY))
		}
	}

	fun colored(color: DyeColor?) = painted(color)

	inline fun <reified T : Tag> blockEntityData(key: String, noinline callback: (itemStack: ItemStack, context: TooltipContext, data: T, acceptor: (line: Component) -> Unit) -> Unit) {
		addNormal { itemStack, context, acceptor ->
			val data = itemStack.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY)
			callback(itemStack, context, data.unsafe.get(key) as? T ?: return@addNormal, acceptor)
		}
	}

	fun blockEntityEnergy(energyKey: String = MatteryBlockEntity.ENERGY_KEY, batteryKey: String = MatteryBlockEntity.BATTERY_KEY) {
		blockEntityData<CompoundTag>(energyKey) { _, context, tag, acceptor ->
			val stored = tag.mapPresent(BlockEnergyStorageImpl.ENERGY_STORED_KEY, Decimal::deserializeNBT)

			if (stored != null) {
				acceptor(
					TranslatableComponent(
						"otm.item.power.storage0",
						stored.formatPower(formatAsReadable = ShiftPressedCond)
					).withStyle(ChatFormatting.GRAY))
			}

			val registry = context.registries()

			if (registry != null) {
				val container = MatteryContainer(1)
				tag.map(batteryKey) { it: Tag -> container.deserializeNBT(registry, it) }

				if (!container[0].isEmpty) {
					acceptor(TranslatableComponent("otm.item.block.stored_battery", container[0].displayName).withStyle(ChatFormatting.GRAY))

					val energy = container[0].energy ?: return@blockEntityData

					if (energy is IMatteryEnergyStorage) {
						batteryLevel(energy, acceptor)
					} else {
						batteryLevel(energy, acceptor)
					}
				}
			}
		}
	}

	fun blockEntityMatter(matterKey: String = MatteryBlockEntity.MATTER_STORAGE_KEY) {
		blockEntityData<CompoundTag>(matterKey) { _, _, tag, acceptor ->
			val stored = tag.mapPresent(MatterStorageImpl.MATTER_STORED_KEY, Decimal::deserializeNBT)

			if (stored != null) {
				acceptor(
					TranslatableComponent(
						"otm.item.matter.storage0",
						stored.formatMatter(formatAsReadable = ShiftPressedCond)
					).withStyle(ChatFormatting.GRAY))
			}
		}
	}

	fun itemEnergy() {
		addNormal { itemStack, _, acceptor ->
			val energy = itemStack.energy ?: return@addNormal

			if (energy is IMatteryEnergyStorage) {
				batteryLevel(energy, acceptor)
			} else {
				batteryLevel(energy, acceptor)
			}
		}
	}

	fun doubleProcessing() {
		add(TranslatableComponent("otm.gui.double_processing").withStyle(ChatFormatting.GRAY))
	}

	fun itemFluid() {
		addNormal { itemStack, _, acceptor ->
			itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.fluidLevel(acceptor)
		}
	}
}
