package moe.nea.firmament.features.debug

import com.mojang.serialization.JsonOps
import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.SkullBlock
import net.minecraft.block.entity.SkullBlockEntity
import net.minecraft.component.DataComponentTypes
import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text
import net.minecraft.text.TextCodecs
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.CustomItemModelEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.events.ScreenChangeEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldKeyboardEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.focusedItemStack
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.skyBlockId

object PowerUserTools : FirmamentFeature {
	override val identifier: String
		get() = "power-user"

	object TConfig : ManagedConfig(identifier, Category.DEV) {
		val showItemIds by toggle("show-item-id") { false }
		val copyItemId by keyBindingWithDefaultUnbound("copy-item-id")
		val copyTexturePackId by keyBindingWithDefaultUnbound("copy-texture-pack-id")
		val copyNbtData by keyBindingWithDefaultUnbound("copy-nbt-data")
		val copyLoreData by keyBindingWithDefaultUnbound("copy-lore")
		val copySkullTexture by keyBindingWithDefaultUnbound("copy-skull-texture")
		val copyEntityData by keyBindingWithDefaultUnbound("entity-data")
		val copyItemStack by keyBindingWithDefaultUnbound("copy-item-stack")
	}

	override val config
		get() = TConfig

	var lastCopiedStack: Pair<ItemStack, Text>? = null
		set(value) {
			field = value
			if (value != null) lastCopiedStackViewTime = true
		}
	var lastCopiedStackViewTime = false

	@Subscribe
	fun resetLastCopiedStack(event: TickEvent) {
		if (!lastCopiedStackViewTime) lastCopiedStack = null
		lastCopiedStackViewTime = false
	}

	@Subscribe
	fun resetLastCopiedStackOnScreenChange(event: ScreenChangeEvent) {
		lastCopiedStack = null
	}

	fun debugFormat(itemStack: ItemStack): Text {
		return Text.literal(itemStack.skyBlockId?.toString() ?: itemStack.toString())
	}

	@Subscribe
	fun onEntityInfo(event: WorldKeyboardEvent) {
		if (!event.matches(TConfig.copyEntityData)) return
		val target = (MC.instance.crosshairTarget as? EntityHitResult)?.entity
		if (target == null) {
			MC.sendChat(Text.translatable("firmament.poweruser.entity.fail"))
			return
		}
		showEntity(target)
	}

	fun showEntity(target: Entity) {
		MC.sendChat(Text.translatable("firmament.poweruser.entity.type", target.type))
		MC.sendChat(Text.translatable("firmament.poweruser.entity.name", target.name))
		MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.position", target.pos))
		if (target is LivingEntity) {
			MC.sendChat(Text.translatable("firmament.poweruser.entity.armor"))
			for (armorItem in target.armorItems) {
				MC.sendChat(Text.translatable("firmament.poweruser.entity.armor.item", debugFormat(armorItem)))
			}
		}
		MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.passengers", target.passengerList.size))
		target.passengerList.forEach {
			showEntity(it)
		}
	}


	@Subscribe
	fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
		if (it.screen !is AccessorHandledScreen) return
		val item = it.screen.focusedItemStack ?: return
		if (it.matches(TConfig.copyItemId)) {
			val sbId = item.skyBlockId
			if (sbId == null) {
				lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skyblockid.fail"))
				return
			}
			ClipboardUtils.setTextContent(sbId.neuItem)
			lastCopiedStack =
				Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
		} else if (it.matches(TConfig.copyTexturePackId)) {
			val model = CustomItemModelEvent.getModelIdentifier(item)
			if (model == null) {
				lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
				return
			}
			ClipboardUtils.setTextContent(model.toString())
			lastCopiedStack =
				Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.modelid", model.toString()))
		} else if (it.matches(TConfig.copyNbtData)) {
			// TODO: copy full nbt
			val nbt = item.get(DataComponentTypes.CUSTOM_DATA)?.nbt?.toPrettyString() ?: "<empty>"
			ClipboardUtils.setTextContent(nbt)
			lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.nbt"))
		} else if (it.matches(TConfig.copyLoreData)) {
			val list = mutableListOf(item.displayNameAccordingToNbt)
			list.addAll(item.loreAccordingToNbt)
			ClipboardUtils.setTextContent(list.joinToString("\n") {
				TextCodecs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().getOrNull().toString()
			})
			lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.lore"))
		} else if (it.matches(TConfig.copySkullTexture)) {
			if (item.item != Items.PLAYER_HEAD) {
				lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-skull"))
				return
			}
			val profile = item.get(DataComponentTypes.PROFILE)
			if (profile == null) {
				lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
				return
			}
			val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile)
			if (skullTexture == null) {
				lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
				return
			}
			ClipboardUtils.setTextContent(skullTexture.toString())
			lastCopiedStack =
				Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()))
			println("Copied skull id: $skullTexture")
		} else if (it.matches(TConfig.copyItemStack)) {
			ClipboardUtils.setTextContent(
				ItemStack.CODEC
					.encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item)
					.orThrow.toPrettyString())
			lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
		}
	}

	@Subscribe
	fun onCopyWorldInfo(it: WorldKeyboardEvent) {
		if (it.matches(TConfig.copySkullTexture)) {
			val p = MC.camera ?: return
			val blockHit = p.raycast(20.0, 0.0f, false) ?: return
			if (blockHit.type != HitResult.Type.BLOCK || blockHit !is BlockHitResult) {
				MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
				return
			}
			val blockAt = p.world.getBlockState(blockHit.blockPos)?.block
			val entity = p.world.getBlockEntity(blockHit.blockPos)
			if (blockAt !is SkullBlock || entity !is SkullBlockEntity || entity.owner == null) {
				MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
				return
			}
			val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!)
			if (id == null) {
				MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
			} else {
				ClipboardUtils.setTextContent(id.toString())
				MC.sendChat(Text.stringifiedTranslatable("firmament.tooltip.copied.skull", id.toString()))
			}
		}
	}

	@Subscribe
	fun addItemId(it: ItemTooltipEvent) {
		if (TConfig.showItemIds) {
			val id = it.stack.skyBlockId ?: return
			it.lines.add(Text.stringifiedTranslatable("firmament.tooltip.skyblockid", id.neuItem))
		}
		val (item, text) = lastCopiedStack ?: return
		if (!ItemStack.areEqual(item, it.stack)) {
			lastCopiedStack = null
			return
		}
		lastCopiedStackViewTime = true
		it.lines.add(text)
	}


}