package ru.dbotthepony.mc.otm.item.weapon

import com.google.common.collect.ImmutableMultimap
import com.google.common.collect.Multimap
import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.tags.BlockTags
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.attributes.Attribute
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.item.Vanishable
import net.minecraft.world.item.enchantment.Enchantment
import net.minecraft.world.item.enchantment.EnchantmentCategory
import net.minecraft.world.item.enchantment.Enchantments
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.common.ToolAction
import net.minecraftforge.common.ToolActions
import net.minecraftforge.common.capabilities.ICapabilityProvider
import ru.dbotthepony.mc.otm.capability.energy.EnergyConsumerItem
import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
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.capability.matteryPlayer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.damageType
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.math.nextVariance
import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.registry.MDamageTypes
import ru.dbotthepony.mc.otm.registry.MatteryDamageSource

class EnergySwordItem : Item(Properties().stacksTo(1).rarity(Rarity.RARE)), Vanishable {
	val chargedAttributes: Multimap<Attribute, AttributeModifier>
	val dischargedAttributes: Multimap<Attribute, AttributeModifier>

	init {
		var builder = ImmutableMultimap.builder<Attribute, AttributeModifier>()

		builder.put(Attributes.ATTACK_DAMAGE, AttributeModifier(BASE_ATTACK_DAMAGE_UUID, "Weapon modifier", 11.0, AttributeModifier.Operation.ADDITION))
		builder.put(Attributes.ATTACK_SPEED, AttributeModifier(BASE_ATTACK_SPEED_UUID, "Weapon modifier", -2.4, AttributeModifier.Operation.ADDITION))

		chargedAttributes = builder.build()

		builder = ImmutableMultimap.builder()

		builder.put(Attributes.ATTACK_DAMAGE, AttributeModifier(BASE_ATTACK_DAMAGE_UUID, "Weapon modifier", 3.5, AttributeModifier.Operation.ADDITION))
		builder.put(Attributes.ATTACK_SPEED, AttributeModifier(BASE_ATTACK_SPEED_UUID, "Weapon modifier", -2.4, AttributeModifier.Operation.ADDITION))

		dischargedAttributes = builder.build()
	}

	override fun isEnchantable(p_41456_: ItemStack): Boolean {
		return p_41456_.count == 1
	}

	override fun canApplyAtEnchantingTable(stack: ItemStack, enchantment: Enchantment): Boolean {
		return enchantment.category == EnchantmentCategory.WEAPON && enchantment != Enchantments.SWEEPING_EDGE
	}

	override fun getEnchantmentValue(stack: ItemStack): Int {
		return 12
	}

	override fun getDestroySpeed(itemStack: ItemStack, blockState: BlockState): Float {
		val energy = itemStack.getCapability(MatteryCapability.ENERGY).orNull() ?: return 1f

		if (blockState.`is`(Blocks.COBWEB)) {
			return if (energy.batteryLevel < COBWEB_POWER_COST) 2f else 25f
		} else if (blockState.`is`(BlockTags.SWORD_EFFICIENT)) {
			return if (energy.batteryLevel < PLANT_POWER_COST) 1.5f else 8f
		} else {
			return 1f
		}
	}

	override fun canAttackBlock(p_41441_: BlockState, p_41442_: Level, p_41443_: BlockPos, p_41444_: Player): Boolean {
		return !p_41444_.isCreative
	}

	override fun hurtEnemy(itemStack: ItemStack, victim: LivingEntity, attacker: LivingEntity): Boolean {
		if (attacker is Player && attacker.isCreative) {
			victim.matteryPlayer?.let {
				if (it.isAndroid) {
					victim.invulnerableTime = 0
					victim.hurt(MatteryDamageSource(attacker.level().registryAccess().damageType(MDamageTypes.EMP), attacker, itemStack), 8f)
				}
			}

			return true
		}

		itemStack.getCapability(MatteryCapability.ENERGY).ifPresentK {
			if (it.extractEnergyExact(ENERGY_PER_SWING, false)) {
				it.extractEnergy(attacker.level().random.nextVariance(ENERGY_PER_SWING_VARIANCE), false)
				victim.matteryPlayer?.let {
					if (it.isAndroid && it.androidEnergy.extractEnergyExact(ENERGY_ZAP, false)) {
						it.androidEnergy.extractEnergy(attacker.level().random.nextVariance(ENERGY_ZAP_VARIANCE), false)
						victim.hurt(MatteryDamageSource(attacker.level().registryAccess().damageType(MDamageTypes.EMP), attacker, itemStack), 8f)
					}
				}
			}
		}

		return true
	}

	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(
		itemStack: ItemStack,
		p_41422_: Level?,
		p_41423_: MutableList<Component>,
		p_41424_: TooltipFlag
	) {
		super.appendHoverText(itemStack, p_41422_, p_41423_, p_41424_)

		ItemEnergyStorageImpl.appendHoverText(itemStack, p_41423_)

		p_41423_.add(DESCRIPTION)
		p_41423_.add(DESCRIPTION2)
		p_41423_.add(DESCRIPTION3)
		p_41423_.add(DESCRIPTION4)
	}

	override fun mineBlock(
		itemStack: ItemStack,
		p_41417_: Level,
		blockState: BlockState,
		p_41419_: BlockPos,
		user: LivingEntity
	): Boolean {
		if (blockState.getDestroySpeed(p_41417_, p_41419_) != 0f && (user !is Player || !user.isCreative)) {
			val energy = itemStack.matteryEnergy

			if (blockState.`is`(BlockTags.SWORD_EFFICIENT)) {
				if (energy?.extractEnergyExact(PLANT_POWER_COST, false) == true)
					energy.extractEnergyExact(user.level().random.nextVariance(PLANT_POWER_COST_VARIANCE), false)
			}

			if (blockState.`is`(Blocks.COBWEB)) {
				if (energy?.extractEnergyExact(COBWEB_POWER_COST, false) == true)
					energy.extractEnergyExact(user.level().random.nextVariance(COBWEB_POWER_COST_VARIANCE), false)
			}
		}

		return true
	}

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

	override fun getAttributeModifiers(
		slot: EquipmentSlot,
		itemStack: ItemStack
	): Multimap<Attribute, AttributeModifier> {
		if (slot != EquipmentSlot.MAINHAND) {
			return ImmutableMultimap.of()
		}

		val energy = itemStack.matteryEnergy

		if (energy != null && energy.batteryLevel >= ENERGY_PER_SWING) {
			return chargedAttributes
		}

		return dischargedAttributes
	}

	override fun canPerformAction(stack: ItemStack, toolAction: ToolAction): Boolean {
		if (stack.matteryEnergy?.extractEnergyExact(ENERGY_PER_SWING, true) == true) {
			return ToolActions.DEFAULT_SWORD_ACTIONS.contains(toolAction)
		}

		return false
	}

	companion object {
		val MAX_ENERGY get() = _MAX_ENERGY.get()
		val ENERGY_ZAP get() = _ENERGY_ZAP.get()
		val ENERGY_ZAP_VARIANCE get() = _ENERGY_ZAP_VARIANCE.get()
		val ENERGY_PER_SWING get() = _ENERGY_PER_SWING.get()
		val ENERGY_PER_SWING_VARIANCE get() = _ENERGY_PER_SWING_VARIANCE.get()
		val COBWEB_POWER_COST get() = _COBWEB_POWER_COST.get()
		val COBWEB_POWER_COST_VARIANCE get() = _COBWEB_POWER_COST_VARIANCE.get()
		val PLANT_POWER_COST get() = _PLANT_POWER_COST.get()
		val PLANT_POWER_COST_VARIANCE get() = _PLANT_POWER_COST_VARIANCE.get()

		private var _MAX_ENERGY: DecimalConfigValue by WriteOnce()
		private var _ENERGY_ZAP: DecimalConfigValue by WriteOnce()
		private var _ENERGY_ZAP_VARIANCE: DecimalConfigValue by WriteOnce()
		private var _ENERGY_PER_SWING: DecimalConfigValue by WriteOnce()
		private var _ENERGY_PER_SWING_VARIANCE: DecimalConfigValue by WriteOnce()
		private var _COBWEB_POWER_COST: DecimalConfigValue by WriteOnce()
		private var _COBWEB_POWER_COST_VARIANCE: DecimalConfigValue by WriteOnce()
		private var _PLANT_POWER_COST: DecimalConfigValue by WriteOnce()
		private var _PLANT_POWER_COST_VARIANCE: DecimalConfigValue by WriteOnce()

		fun registerConfig(builder: ForgeConfigSpec.Builder) {
			builder.comment("Energy sword values").push("EnergySword")

			_MAX_ENERGY = builder.defineDecimal("MAX_ENERGY", Decimal(500_000), Decimal.ZERO)
			_ENERGY_ZAP = builder.comment("Extra energy required when hitting androids").defineDecimal("ENERGY_ZAP", Decimal(4_000), Decimal.ZERO)
			_ENERGY_ZAP_VARIANCE = builder.comment("Random deviation from ENERGY_ZAP").defineDecimal("ENERGY_ZAP_VARIANCE", Decimal(800), Decimal.ZERO)
			_ENERGY_PER_SWING = builder.defineDecimal("ENERGY_PER_SWING", Decimal(2_000), Decimal.ZERO)
			_ENERGY_PER_SWING_VARIANCE = builder.comment("Random deviation from ENERGY_PER_SWING").defineDecimal("ENERGY_PER_SWING_VARIANCE", Decimal(500), Decimal.ZERO)
			_COBWEB_POWER_COST = builder.defineDecimal("COBWEB_POWER_COST", Decimal(2_500), Decimal.ZERO)
			_COBWEB_POWER_COST_VARIANCE = builder.comment("Random deviation from COBWEB_POWER_COST").defineDecimal("COBWEB_POWER_COST_VARIANCE", Decimal(500), Decimal.ZERO)
			_PLANT_POWER_COST = builder.defineDecimal("PLANT_POWER_COST", Decimal(500), Decimal.ZERO)
			_PLANT_POWER_COST_VARIANCE = builder.comment("Random deviation from PLANT_POWER_COST").defineDecimal("PLANT_POWER_COST_VARIANCE", Decimal(100), Decimal.ZERO)

			builder.pop()
		}

		@JvmStatic
		fun getSweepingDamageRatioHook(ply: LivingEntity): Float? {
			if (ply.mainHandItem.item is EnergySwordItem && ply.mainHandItem.matteryEnergy?.extractEnergyExact(ENERGY_PER_SWING, true) == true) {
				return 1f
			}

			return null
		}

		private val DESCRIPTION = TranslatableComponent("item.overdrive_that_matters.energy_sword.desc").withStyle(ChatFormatting.DARK_GRAY)
		private val DESCRIPTION2 = TranslatableComponent("item.overdrive_that_matters.energy_sword.desc2").withStyle(ChatFormatting.DARK_GRAY)
		private val DESCRIPTION3 = TranslatableComponent("item.overdrive_that_matters.energy_sword.desc3").withStyle(ChatFormatting.DARK_GRAY)
		private val DESCRIPTION4 = TranslatableComponent("item.overdrive_that_matters.energy_sword.desc4").withStyle(ChatFormatting.DARK_GRAY)
	}
}
