package ru.dbotthepony.mc.otm.item

import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.ChatFormatting
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.functions.LootItemFunction
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.batteryLevel
import ru.dbotthepony.mc.otm.capability.energy.getBarColor
import ru.dbotthepony.mc.otm.capability.energy.getBarWidth
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
import ru.dbotthepony.mc.otm.registry.CapabilitiesRegisterListener
import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes
import ru.dbotthepony.mc.otm.registry.data.MItemFunctionTypes
import java.util.*

class ProceduralBatteryItem : MatteryItem(Properties().stacksTo(1)), CapabilitiesRegisterListener {
	class Cap(stack: ItemStack) : ItemEnergyStorageImpl(stack) {
		override var maxInput: Decimal
			get() = itemStack[MDataComponentTypes.MAX_BATTERY_INPUT] ?: Decimal.ZERO
			set(value) { itemStack[MDataComponentTypes.MAX_BATTERY_INPUT] = value }

		override var maxOutput: Decimal
			get() = itemStack[MDataComponentTypes.MAX_BATTERY_OUTPUT] ?: Decimal.ZERO
			set(value) { itemStack[MDataComponentTypes.MAX_BATTERY_OUTPUT] = value }

		override var maxBatteryLevel: Decimal
			get() = itemStack[MDataComponentTypes.MAX_BATTERY_LEVEL] ?: Decimal.ZERO
			set(value) { itemStack[MDataComponentTypes.MAX_BATTERY_LEVEL] = value }

		override val energyFlow: FlowDirection
			get() = FlowDirection.BI_DIRECTIONAL

		override val initialBatteryLevel: Decimal
			get() = maxBatteryLevel
	}

	override fun isBarVisible(p_150899_: ItemStack): Boolean {
		return p_150899_.matteryEnergy != null
	}

	override fun getBarWidth(p_150900_: ItemStack): Int {
		return p_150900_.matteryEnergy?.getBarWidth() ?: super.getBarWidth(p_150900_)
	}

	override fun getBarColor(p_150901_: ItemStack): Int {
		return p_150901_.matteryEnergy?.getBarColor() ?: super.getBarColor(p_150901_)
	}

	init {
		tooltips.addNormal { itemStack, context, acceptor ->
			val energy = itemStack.matteryEnergy

			if (energy is Cap) {
				if (energy.maxInput.isZero && energy.maxOutput.isZero && energy.maxBatteryLevel.isZero) {
					acceptor(TranslatableComponent("$descriptionId.desc").withStyle(ChatFormatting.GRAY))
				} else {
					batteryLevel(energy, acceptor)
				}
			}
		}
	}

	data class Randomizer(
		val maxBatteryLevel: DecimalProvider,
		val batteryLevel: Optional<DecimalProvider>,
		val maxInput: DecimalProvider,
		val maxOutput: Optional<DecimalProvider>
	) : LootItemFunction, LootItemFunction.Builder {
		constructor(
			maxBatteryLevel: DecimalProvider,
			batteryLevel: DecimalProvider? = null,
			maxInput: DecimalProvider,
			maxOutput: DecimalProvider? = null
		) : this(maxBatteryLevel, Optional.ofNullable(batteryLevel), maxInput, Optional.ofNullable(maxOutput))

		override fun apply(t: ItemStack, u: LootContext): ItemStack {
			val data = Cap(t)

			if (maxOutput.isPresent) {
				data.maxInput = maxInput.sample(u.random)
				data.maxOutput = maxOutput.get().sample(u.random)
			} else {
				data.maxInput = maxInput.sample(u.random)
				data.maxOutput = data.maxInput
			}

			data.maxBatteryLevel = maxBatteryLevel.sample(u.random)

			if (batteryLevel.isPresent) {
				data.batteryLevel = batteryLevel.get().sample(u.random).coerceAtMost(data.maxBatteryLevel)
			} else {
				data.batteryLevel = data.maxBatteryLevel
			}

			return t
		}

		override fun getType(): LootItemFunctionType<Randomizer> {
			return MItemFunctionTypes.PROCEDURAL_BATTERY
		}

		override fun build(): LootItemFunction {
			return this
		}

		companion object {
			val CODEC: MapCodec<Randomizer> by lazy {
				RecordCodecBuilder.mapCodec {
					it.group(
						DecimalProvider.CODEC.fieldOf("maxBatteryLevel").forGetter(Randomizer::maxBatteryLevel),
						DecimalProvider.CODEC.optionalFieldOf("batteryLevel").forGetter(Randomizer::batteryLevel),
						DecimalProvider.CODEC.fieldOf("maxInput").forGetter(Randomizer::maxInput),
						DecimalProvider.CODEC.optionalFieldOf("maxOutput").forGetter(Randomizer::maxOutput),
					).apply(it, ::Randomizer)
				}
			}
		}
	}

	override fun registerCapabilities(event: RegisterCapabilitiesEvent) {
		event.registerItem(Capabilities.EnergyStorage.ITEM, { o, _ -> Cap(o) }, this)
		event.registerItem(MatteryCapability.ITEM_ENERGY, { o, _ -> Cap(o) }, this)
	}
}
