package ru.dbotthepony.mc.otm.block.entity.tech

import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.Enchantments
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage.Companion.XP_TO_LIQUID_RATIO
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.item.EssenceCapsuleItem
import ru.dbotthepony.mc.otm.menu.tech.EssenceStorageMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MFluids

class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ESSENCE_STORAGE, blockPos, blockState), IFluidHandler {
	var experienceStored = 0L
		set(value) {
			require(value >= 0L) { "Negative experience: $value" }
			field = value
			markDirtyFast()
		}

	val capsuleContainer = MatteryContainer(::markDirtyFast, 1)
	val servoContainer = MatteryContainer(::markDirtyFast, 1)
	val mendingContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)

	init {
		savetables.long(::experienceStored)
		savetables.stateful(::capsuleContainer)
		savetables.stateful(::servoContainer)
		savetables.stateful(::mendingContainer)
	}

	override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
		return EssenceStorageMenu(containerID, inventory, this)
	}

	val itemConfig = ConfigurableItemHandler(
		inputOutput = CombinedItemHandler(
			capsuleContainer.handler(HandlerFilter.OnlyIn.and(object : HandlerFilter {
				override fun canInsert(slot: Int, stack: ItemStack): Boolean {
					return stack.item is EssenceCapsuleItem
				}
			})),
			mendingContainer.handler(object : HandlerFilter {
				override fun canInsert(slot: Int, stack: ItemStack): Boolean {
					return stack.isDamaged && stack.getEnchantmentLevel(Enchantments.MENDING) > 0
				}

				override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
					return !stack.isDamaged || experienceStored <= 0
				}
			})
		)
	)

	override fun getTanks(): Int {
		return 1
	}

	override fun getFluidInTank(tank: Int): FluidStack {
		if (tank != 0)
			return FluidStack.EMPTY

		if (experienceStored >= 2_000_000_000L)
			return FluidStack(MFluids.LIQUID_XP, 2_000_000_000)

		return FluidStack(MFluids.LIQUID_XP, experienceStored.toInt())
	}

	override fun getTankCapacity(tank: Int): Int {
		if (tank != 0) return 0
		return Int.MAX_VALUE
	}

	override fun isFluidValid(tank: Int, stack: FluidStack): Boolean {
		return tank == 0 && stack.fluid.fluidType == MFluids.LIQUID_XP_TYPE
	}

	override fun fill(resource: FluidStack, action: IFluidHandler.FluidAction): Int {
		if (resource.fluid.fluidType != MFluids.LIQUID_XP_TYPE || resource.amount <= 0)
			return 0

		val actualFill = resource.amount / XP_TO_LIQUID_RATIO * XP_TO_LIQUID_RATIO

		if (action.execute())
			experienceStored += actualFill / XP_TO_LIQUID_RATIO

		return actualFill
	}

	override fun drain(resource: FluidStack, action: IFluidHandler.FluidAction): FluidStack {
		if (resource.fluid.fluidType != MFluids.LIQUID_XP_TYPE)
			return FluidStack.EMPTY

		return drain(resource.amount, action)
	}

	override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction): FluidStack {
		val stored = experienceStored * XP_TO_LIQUID_RATIO
		val actualStored = if (stored >= Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt()
		val actualDrain = maxDrain.coerceAtMost(actualStored).let { it / XP_TO_LIQUID_RATIO * XP_TO_LIQUID_RATIO }

		if (actualDrain <= 0)
			return FluidStack.EMPTY

		if (action.execute())
			experienceStored -= actualDrain / XP_TO_LIQUID_RATIO

		return FluidStack(MFluids.LIQUID_XP, actualDrain)
	}

	override fun tick() {
		super.tick()

		if (!redstoneControl.isBlockedByRedstone) {
			val capsule = capsuleContainer[0]

			if (!capsule.isEmpty && capsule.item is EssenceCapsuleItem) {
				experienceStored += EssenceCapsuleItem.experienceStored(capsule)
				capsuleContainer.clearContent()
			}

			val repairStack = mendingContainer[0]

			if (!repairStack.isEmpty && repairStack.isDamaged) {
				if (experienceStored > 0) {
					val dmgPerExp = repairStack.xpRepairRatio
					var repairPoints = dmgPerExp.toInt()

					val diff = dmgPerExp - repairPoints.toFloat()
					if (diff > 0f) {
						repairPoints += if ((level?.random?.nextFloat() ?: 1f) <= diff) 1 else 0
					}

					experienceStored -= 1
					repairStack.damageValue -= repairPoints
				}
			}
		}
	}
}
