package ru.dbotthepony.mc.otm.item

import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.Level
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.minecraftforge.common.capabilities.ICapabilityProvider
import ru.dbotthepony.mc.otm.capability.FlowDirection
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.core.math.set
import ru.dbotthepony.mc.otm.core.nbt.mapPresent
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.data.Codec2Serializer
import ru.dbotthepony.mc.otm.data.DecimalProvider
import ru.dbotthepony.mc.otm.registry.MItemFunctionTypes
import java.util.Optional

class ProceduralBatteryItem : Item(Properties().stacksTo(1)) {
	class Cap(stack: ItemStack) : ItemEnergyStorageImpl(stack) {
		override var maxInput: Decimal
			get() = itemStack.tag?.mapPresent("maxInput", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
			set(value) { itemStack.tagNotNull["maxInput"] = value }

		override var maxOutput: Decimal
			get() = itemStack.tag?.mapPresent("maxOutput", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
			set(value) { itemStack.tagNotNull["maxOutput"] = value }

		override var maxBatteryLevel: Decimal
			get() = itemStack.tag?.mapPresent("maxBatteryLevel", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
			set(value) { itemStack.tagNotNull["maxBatteryLevel"] = 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_)
	}

	override fun appendHoverText(p_41421_: ItemStack, p_41422_: Level?, p_41423_: MutableList<Component>, p_41424_: TooltipFlag) {
		super.appendHoverText(p_41421_, p_41422_, p_41423_, p_41424_)

		val energy = p_41421_.matteryEnergy

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

	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 {
			return MItemFunctionTypes.PROCEDURAL_BATTERY
		}

		override fun build(): LootItemFunction {
			return this
		}

		companion object {
			val SERIALIZER by lazy {
				Codec2Serializer<Randomizer>(
					RecordCodecBuilder.create {
						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 initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
		return Cap(stack)
	}
}
