import { decrementItemInInventory } from '../util/utils';
import { ItemEnchantableComponent, ItemStack, system, world } from '@minecraft/server';

const enchantmentTime = {
	aqua_affinity: { base: 130, multiplier: 0 },
	bane_of_arthropods: { base: 90, multiplier: 15 },
	binding: { base: 210, multiplier: 0 },
	blast_protection: { base: 110, multiplier: 20 },
	breach: { base: 160, multiplier: 25 },
	channeling: { base: 210, multiplier: 0 },
	density: { base: 150, multiplier: 20 },
	depth_strider: { base: 140, multiplier: 25 },
	efficiency: { base: 150, multiplier: 30 },
	feather_falling: { base: 150, multiplier: 30 },
	fire_aspect: { base: 130, multiplier: 20 },
	fire_protection: { base: 110, multiplier: 20 },
	flame: { base: 160, multiplier: 0 },
	fortune: { base: 150, multiplier: 35 },
	frost_walker: { base: 140, multiplier: 20 },
	impaling: { base: 150, multiplier: 25 },
	infinity: { base: 210, multiplier: 0 },
	knockback: { base: 100, multiplier: 15 },
	looting: { base: 150, multiplier: 25 },
	loyalty: { base: 140, multiplier: 20 },
	luck_of_the_sea: { base: 130, multiplier: 20 },
	lure: { base: 130, multiplier: 20 },
	mending: { base: 260, multiplier: 0 },
	multishot: { base: 160, multiplier: 0 },
	piercing: { base: 130, multiplier: 20 },
	power: { base: 150, multiplier: 30 },
	projectile_protection: { base: 110, multiplier: 20 },
	protection: { base: 150, multiplier: 30 },
	punch: { base: 100, multiplier: 15 },
	quick_charge: { base: 130, multiplier: 20 },
	respiration: { base: 140, multiplier: 20 },
	riptide: { base: 150, multiplier: 25 },
	sharpness: { base: 150, multiplier: 35 },
	silk_touch: { base: 210, multiplier: 0 },
	smite: { base: 110, multiplier: 20 },
	soul_speed: { base: 140, multiplier: 20 },
	swift_sneak: { base: 160, multiplier: 20 },
	thorns: { base: 150, multiplier: 25 },
	unbreaking: { base: 150, multiplier: 25 },
	vanishing: { base: 210, multiplier: 0 },
	wind_burst: { base: 160, multiplier: 25 },
}
const inkFillBar = {
	"mr:ink_cartridge": 200,
	"minecraft:ink_sac": 50
}
export class McfPrinter {
	static getPrinterData(entity) {
		return {
			inkProgress: entity.getDynamicProperty("mr:printer_ink_progress") ?? 0,
			inkTotal: entity.getDynamicProperty("mr:printer_ink_total") ?? 0,
			progressBar: entity.getDynamicProperty("mr:printer_progress_bar") ?? 0,
			progressTotal: entity.getDynamicProperty("mr:printer_progress_total") ?? 0,
			bookData: entity.getDynamicProperty("mr:printer_book_data") ?? ""
		};
	}
	static setPrinterData(entity, data) {
		entity.setDynamicProperty("mr:printer_ink_progress", data.inkProgress);
		entity.setDynamicProperty("mr:printer_ink_total", data.inkTotal);
		entity.setDynamicProperty("mr:printer_progress_bar", data.progressBar);
		entity.setDynamicProperty("mr:printer_progress_total", data.progressTotal);
		entity.setDynamicProperty("mr:printer_book_data", data.bookData ?? "");
	}
	static calculatePrintTime(enchants) {
		let totalTime = 0;
		if (enchants?.length > 0) {
			for (const e of enchants) {
				const data = enchantmentTime[e.type.id];
				if (data) {
					const level = e.level;
					if (data.multiplier === 0) totalTime += data.base;
					else totalTime += data.base + (data.multiplier * (level - 1));
				}
			}
		}
		return totalTime;
	}
	static getEnchantments(item) {
		return item?.getComponent(ItemEnchantableComponent.componentId)?.getEnchantments() || [];
	}
	static getBookData(item) {
		if (item?.typeId !== "minecraft:enchanted_book") return null;
		const enchants = this.getEnchantments(item);
		return `${item.typeId}|${enchants.map(e => `${e.type.id}:${e.level}`).join(',')}`;
	}
	static booksMatch(currentBook, storedBook) {
		if (!currentBook && !storedBook) return true;
   		if (!currentBook || !storedBook) return false;
		return currentBook === storedBook;
	}
	static updateInkDisplay(inventory, inkProgress, inkTotal) {
		const inkVisual = Math.max(1, Math.ceil((inkProgress / inkTotal) * 64));
		inventory.setItem(3, new ItemStack("mr:timer", inkVisual));
	}
	static updateProgressDisplay(inventory, progressBar, progressTotal) {
		const progressVisual = Math.max(1, Math.ceil((progressBar / progressTotal) * 64));
		inventory.setItem(4, new ItemStack("mr:timer", progressVisual));
	}
	static showProgress(player, entity) {
		const { inkProgress, inkTotal, progressBar, progressTotal } = this.getPrinterData(entity);
		player.onScreenDisplay.setActionBar(`§7Ink: ${inkProgress.toFixed(1)}/${inkTotal}\nPrint: ${progressBar.toFixed(1)}/${progressTotal}`);
	}
	static stopPrinting(entity, inventory, printerData) {
		printerData.progressBar = 0;
    	printerData.progressTotal = 0;
		this.setPrinterData(entity, printerData);
		inventory.setItem(4, new ItemStack("mr:timer", 1));
		entity.triggerEvent("mr:stop");
	}
	static handleCheckEvent(entity, inventory, printerData) {
		const item0 = inventory.getItem(0);
		const item1 = inventory.getItem(1);
		let shouldConsumeInk = false;

		if (printerData.inkProgress <= 0) {
			const inkTime = inkFillBar[item0?.typeId];
			if (!inkTime) return;
			printerData.inkProgress = inkTime;
			printerData.inkTotal = inkTime;
			shouldConsumeInk = true;
		}
		const bookData = this.getBookData(item1);
		if (bookData) printerData.bookData = bookData;
		const totalTime = this.calculatePrintTime(this.getEnchantments(item1));
		if (totalTime > 0 && printerData.inkProgress > 0 && !inventory.getItem(2)) {
			printerData.progressBar = 0;
			printerData.progressTotal = totalTime;
			this.updateInkDisplay(inventory, printerData.inkProgress, printerData.inkTotal);
			this.updateProgressDisplay(inventory, 0, totalTime);
			if (shouldConsumeInk) decrementItemInInventory(entity, 0);
			entity.triggerEvent("mr:start");
			this.setPrinterData(entity, printerData);
		}
	}
	static handleUpdateTimerEvent(entity, inventory, printerData) {
		const item0 = inventory.getItem(0);
		const item1 = inventory.getItem(1);
		if (!item1 || !printerData.bookData || inventory.getItem(2)) {
			this.stopPrinting(entity, inventory, printerData);
			return;
		}
		const currentBook = this.getBookData(item1);
		if (!currentBook || !this.booksMatch(currentBook, printerData.bookData)) {
			this.stopPrinting(entity, inventory, printerData);
			return;
		}
		if (printerData.inkProgress <= 0) {
			const inkTime = inkFillBar[item0?.typeId];
			if (inkTime && item0) {
				printerData.inkProgress = inkTime;
				printerData.inkTotal = inkTime;
				decrementItemInInventory(entity, 0);
				this.updateInkDisplay(inventory, printerData.inkProgress, printerData.inkTotal);
			} else return this.stopPrinting(entity, inventory, printerData);
		}
		if (printerData.progressBar >= printerData.progressTotal) {
			inventory.setItem(2, item1.clone());
			printerData.progressBar = 0;
			printerData.progressTotal = 0;
			this.stopPrinting(entity, inventory, printerData);
			this.setPrinterData(entity, printerData);
			return;
		}
		this.updateInkDisplay(inventory, printerData.inkProgress, printerData.inkTotal);
		this.updateProgressDisplay(inventory, printerData.progressBar, printerData.progressTotal);
		printerData.inkProgress -= 0.1;
		printerData.progressBar += 0.1;
		this.setPrinterData(entity, printerData);
	}
	static registerEvent() {
		world.afterEvents.dataDrivenEntityTrigger.subscribe(({ entity, eventId }) => {
			if (!entity?.isValid) return;
			const inventory = entity.getComponent("inventory").container;
			const printerData = this.getPrinterData(entity);
			if (eventId === "mr:check") this.handleCheckEvent(entity, inventory, printerData);
			if (eventId === "mr:update_timer") this.handleUpdateTimerEvent(entity, inventory, printerData)
		}, {
			entityTypes: ["mr:printer"],
			eventTypes: ["mr:check", "mr:update_timer"]
		})
	}
}
class PrinterProgressManager {
	static activePlayers = new Map();
	static generalInterval = null;
	static initGeneralInterval() {
		if (this.generalInterval) return;
		this.generalInterval = system.runInterval(() => {
			for (const [player, entity] of this.activePlayers) {
				if (!player?.isValid || !entity?.isValid) {
					this.activePlayers.delete(player);
					continue;
				}
				McfPrinter.showProgress(player, entity);
			}
			if (this.activePlayers.size === 0) this.stopGeneralInterval();
		}, 2);
	}
	static stopGeneralInterval() {
		if (this.generalInterval) {
			system.clearRun(this.generalInterval);
			this.generalInterval = null;
		}
	}
	static toggleProgress(player, entity) {
		if (this.activePlayers.has(player)) {
			this.activePlayers.delete(player);
			player.sendMessage({ translate: "mcf.printer.message_disabled" });
		} else {
			this.activePlayers.set(player, entity);
			player.sendMessage({ translate: "mcf.printer.message_enabled" });
			this.initGeneralInterval();
		}
	}
}
export class McfPrinterBlock {
	onPlayerInteract(e) {
		const { player, block, dimension } = e;
		const printerEntity = dimension.getEntitiesAtBlockLocation(block.location).find(en => en.typeId === "mr:printer");
		if (printerEntity) {
			PrinterProgressManager.toggleProgress(player, printerEntity);
		}
	}
}