package ru.dbotthepony.mc.otm.item

import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.*
import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.capabilities.ICapabilityProvider
import ru.dbotthepony.mc.otm.config.BatteryBalanceValues
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.EnergyCapacitorItem
import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.getBarColor
import ru.dbotthepony.mc.otm.capability.energy.getBarWidth
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.registry.MDamageTypes
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.MatteryDamageSource
import ru.dbotthepony.mc.otm.runIfClient
import kotlin.math.roundToInt

open class BatteryItem : Item {
	private inner class Power(stack: ItemStack) : EnergyCapacitorItem(stack, this@BatteryItem.capacity, this@BatteryItem.receive, this@BatteryItem.extract, initialBatteryLevel = this@BatteryItem.initialBatteryLevel) {
		override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
			if (isCreative) return howMuch
			return super.extractEnergy(howMuch, simulate)
		}

		override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
			if (isCreative) return howMuch
			return super.receiveEnergy(howMuch, simulate)
		}

		override val missingPower: Decimal
			get() {
			return if (isCreative) Decimal.LONG_MAX_VALUE else super.missingPower
		}

		fun maxPower() {
			batteryLevel = maxBatteryLevel
		}

		override var batteryLevel: Decimal
			get() { return if (isCreative) Decimal.LONG_MAX_VALUE else super.batteryLevel }
			set(value) { super.batteryLevel = value }
	}

	private val isCreative: Boolean

	val _capacity: () -> Decimal
	val _receive: () -> Decimal
	val _extract: () -> Decimal
	val _initialBatteryLevel: () -> Decimal

	val capacity: Decimal
		get() = _capacity.invoke()
	val receive: Decimal
		get() = _receive.invoke()
	val extract: Decimal
		get() = _extract.invoke()
	val initialBatteryLevel: Decimal
		get() = _initialBatteryLevel.invoke()

	constructor(
		storage: Decimal,
		receive: Decimal,
		extract: Decimal = receive,
		initialBatteryLevel: Decimal = Decimal.ZERO
	) : super(Properties().stacksTo(1)) {
		isCreative = false
		this._capacity = { storage }
		this._receive = { receive }
		this._extract = { extract }
		this._initialBatteryLevel = { initialBatteryLevel }
	}

	constructor(
		storage: () -> Decimal,
		receive: () -> Decimal,
		extract: () -> Decimal = receive,
		initialBatteryLevel: () -> Decimal = { Decimal.ZERO }
	) : super(Properties().stacksTo(1)) {
		isCreative = false
		this._capacity = storage
		this._receive = receive
		this._extract = extract
		this._initialBatteryLevel = initialBatteryLevel
	}

	constructor(values: BatteryBalanceValues) : super(Properties().stacksTo(1)) {
		isCreative = false
		this._capacity = values::capacity
		this._receive = values::receive
		this._extract = values::extract
		this._initialBatteryLevel = values::initialBatteryLevel
	}

	constructor() : super(Properties().stacksTo(1).rarity(Rarity.EPIC)) {
		isCreative = true
		_capacity = { Decimal.LONG_MAX_VALUE }
		_receive = { Decimal.LONG_MAX_VALUE }
		_extract = { Decimal.LONG_MAX_VALUE }
		_initialBatteryLevel = { Decimal.LONG_MAX_VALUE }
	}

	override fun isBarVisible(p_150899_: ItemStack): Boolean {
		if (isCreative)
			return false

		return p_150899_.matteryEnergy != null
	}

	override fun getBarWidth(p_150900_: ItemStack): Int {
		if (isCreative)
			return 13

		return p_150900_.matteryEnergy?.getBarWidth() ?: super.getBarWidth(p_150900_)
	}

	override fun getBarColor(p_150901_: ItemStack): Int {
		if (isCreative)
			return 0

		return p_150901_.matteryEnergy?.getBarColor() ?: super.getBarColor(p_150901_)
	}

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

		if (isCreative) {
			p_41423_.add(INFINITE_STORAGE)
			p_41423_.add(TranslatableComponent("otm.item.power.infinite.throughput").withStyle(ChatFormatting.GRAY))
		} else {
			ItemEnergyStorageImpl.appendHoverText(stack, p_41423_)
		}
	}

	override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
		return Power(stack)
	}

	companion object {
		private val INFINITE_STORAGE: Component =
			TranslatableComponent("otm.item.power.infinite.storage").withStyle(ChatFormatting.GRAY)
	}
}

class CrudeBatteryItem : BatteryItem(ItemsConfig.Batteries.CRUDE) {
	override fun appendHoverText(
		stack: ItemStack,
		p_41422_: Level?,
		p_41423_: MutableList<Component>,
		p_41424_: TooltipFlag
	) {
		super.appendHoverText(stack, p_41422_, p_41423_, p_41424_)

		val isAndroid = runIfClient(false) { minecraft.player?.matteryPlayer?.isAndroid ?: false }

		if (isAndroid) {
			p_41423_.add(TranslatableComponent("otm.gui.crude_battery.replace_in_world").withStyle(ChatFormatting.GRAY))
			p_41423_.add(TranslatableComponent("otm.gui.crude_battery.replace_in_world_warning").withStyle(ChatFormatting.DARK_RED))
		}
	}

	override fun getUseAnimation(itemStack: ItemStack): UseAnim {
		return UseAnim.BOW
	}

	override fun getUseDuration(itemStack: ItemStack): Int {
		return 100
	}

	override fun use(level: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
		if (player.matteryPlayer?.isAndroid == true) {
			player.startUsingItem(hand)
			return InteractionResultHolder.consume(player.getItemInHand(hand))
		}

		return super.use(level, player, hand)
	}

	override fun finishUsingItem(itemStack: ItemStack, level: Level, player: LivingEntity): ItemStack {
		if (player !is Player) return super.finishUsingItem(itemStack, level, player)
		val mattery = player.matteryPlayer ?: return super.finishUsingItem(itemStack, level, player)
		if (!mattery.isAndroid) return super.finishUsingItem(itemStack, level, player)

		if (player is ServerPlayer) {
			if (!mattery.androidEnergy.item.isEmpty) {
				mattery.androidEnergy.item.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
					it.extractEnergy((it.maxEnergyStored * level.random.nextFloat() * .2f).roundToInt(), false)
				}

				mattery.dropBattery()
			}

			val copyStack = itemStack.copy()
			copyStack.count = 1
			mattery.androidEnergy.item = copyStack

			val extraDamageMult = level.random.nextFloat()
			player.hurt(MatteryDamageSource(level.registryAccess().damageType(MDamageTypes.EMP), inflictor = itemStack), 1.5f + extraDamageMult * 3.5f)

			val debuffDuration = 100 + (100 * (1f - extraDamageMult)).roundToInt()
			player.addEffect(MobEffectInstance(MobEffects.BLINDNESS, debuffDuration), player)
			player.addEffect(MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, debuffDuration, 1), player)
			player.addEffect(MobEffectInstance(MobEffects.DIG_SLOWDOWN, debuffDuration, 2), player)
			player.addEffect(MobEffectInstance(MobEffects.WEAKNESS, debuffDuration, 2), player)
		}

		if (!player.abilities.instabuild)
			itemStack.shrink(1)

		return itemStack
	}
}
