import { M5Character } from "../actors/M5Character";
import M5ModAggregate from "../actors/M5ModAggregate";
import { enumKeys, M5Attributes, M5ModOperation, M5ModPair, M5ModType, M5RollData, M5RollResult, M5Stats } from "../M5Base";
import { M5Roll } from "../rolls/M5Roll";

export class M5Item extends Item {
	static readonly SKILL = "skill";

	prepareDerivedData() {
		const itemId: string = (this as any).id;
		const itemType: string = (this as any).type;
		const actor = this.actor as any;
		const character = actor as M5Character;
		const itemData = (this as any).system;
		const calc = itemData.calc;

	if (itemType === "item") {
		calc.containers = null;

		if (actor) {
			const actorCalc = actor.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
			if (actorCalc) {
				calc.containers = actorCalc.gear.containers;
			}
			const container = character.getItem(itemData.containerId);
			//console.log("M5Item.prepareDerivedData:containers", itemData, container?.system)
			if (container) {
				container.prepareDerivedData();
				const containerData = container.system;
			}	
		}	
	} else if (itemType === "skill") {
			calc.fw = itemData.fw;
			calc.bonus = 0;

			let pairs: Array<M5ModPair> = [
				{
					source: (this as any).name,
					mod: {
						type: M5ModType.SKILL,
						id: itemId,
						operation: M5ModOperation.SET,
						value: itemData.fw,
					},
				},
			];

			if (character) {
				const actorCalc = character.derivedData({ skills: true, weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
				if (actorCalc?.skillMods && Object.keys(actorCalc.skillMods).indexOf(itemId) !== -1) {
					pairs = pairs.concat(actorCalc.skillMods[itemId]);
				}

				if (itemData?.attribute && itemData.attribute !== "") {
					pairs.push({
						source: (this as any).name,
						mod: {
							type: M5ModType.SKILL,
							id: itemId,
							operation: M5ModOperation.ADD,
							value: actorCalc.attributes[itemData.attribute].bonus,
						},
					});
				}
			}

			const res = M5ModAggregate.processPairs(pairs);
			res.mods.forEach((mod) => {
				if ([M5ModOperation.SET, M5ModOperation.FIXED].includes(mod.operation)) calc.fw = mod.value;
				else if ([M5ModOperation.SUBTRACT].includes(mod.operation)) calc.bonus -= mod.value;
				else if ([M5ModOperation.ADD].includes(mod.operation)) calc.bonus += mod.value;
			});

			calc.ew = calc.fw + calc.bonus;
			calc.sources = res.mods;
		} else if (itemType === "weapon") {
			calc.fw = 0;
			calc.bonus = 0;
			calc.special = itemData.special ? 2 : 0;
			calc.ew = calc.special + itemData.stats.attackBonus;
			calc.combatSkills = null;

			if (actor) {
				const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
				if (actorCalc) {
					calc.ew += actorCalc.stats.attackBonus.value;
					calc.combatSkills = actorCalc.skills.combat;
				}

				const skill = character.getItem(itemData.skillId);
				//console.log("M5Item.prepareDerivedData:weapon", itemData, skill?.system)
				if (skill) {
					skill.prepareDerivedData();
					const skillData = skill.system;
					calc.ew += skillData.calc.ew;
					calc.bonus += skillData.calc.bonus;
					calc.fw += skillData.fw;
				}
			}
		} else if (itemType === "defensiveWeapon") {
			calc.fw = 0;
			calc.bonus = 0;
			calc.special = itemData.special ? 2 : 0;
			calc.ew = calc.special + itemData.stats.defenseBonus;
			calc.combatSkills = null;

			if (actor) {
				const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
				if (actorCalc) {
					calc.ew += actorCalc.stats.defense.value + actorCalc.stats.defenseBonus.value;
					calc.combatSkills = actorCalc.skills.combat;
				}

				const skill = character.getItem(itemData.skillId);
				//console.log("M5Item.prepareDerivedData:weapon", itemData, skill?.system)
				if (skill) {
					skill.prepareDerivedData();
					const skillData = skill.system;
					calc.ew += skillData.calc.ew;
					calc.bonus += skillData.calc.bonus;
					calc.fw += skillData.fw;
				}
			}
		} else if (itemType === "armor") {
			itemData.mods[0] = { type: "stat", id: "defenseBonus", operation: "add", value: itemData.stats.defenseBonus };
			itemData.mods[1] = { type: "stat", id: "attackBonus", operation: "add", value: itemData.stats.attackBonus };
			itemData.mods[2] = { type: "stat", id: "movement", operation: "add", value: itemData.stats.movementBonus };
			itemData.mods[3] = { type: "attribute", id: "gw", operation: "add100", value: itemData.attributeMod.gw };
			itemData.mods[4] = { type: "stat", id: "lpProtection", operation: "set", value: itemData.lpProtection };
			itemData.mods[5] = { type: "stat", id: "apProtection", operation: "set", value: itemData.apProtection };
		} else if (itemType === "spell") {
			calc.fw = 0;
			if (actor) {
				const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
				if (actorCalc) {
					calc.ew = actorCalc.stats.spellCasting.value;
				}
			}
		} else if (itemType === "kampfkunst") {
			calc.fw = 0;
			calc.bonus = 0;
			calc.ew = 0;
			calc.generalSkills = null;

			if (actor) {
				const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
				if (actorCalc) {
					calc.generalSkills = actorCalc.skills.general;
				}

				const skill = character.getItem(itemData.skillId);
				if (skill) {
					skill.prepareDerivedData();
					const skillData = skill.system;
					calc.ew = skillData.calc.ew;
					calc.fw = skillData.fw + calc.bonus;
					itemData.rolls.formulas[0].label = skill.name;
				}
			}
		}
		if (itemData?.mods) {
			calc.mods = {};
			for (let modKey in itemData.mods) {
				if (itemData.mods[modKey].type === M5ModType.SKILL && itemData.mods[modKey].id?.includes("midgard5")) {
					itemData.mods[modKey].id = actor?.items.find((x) => x.name === game["i18n"].localize(itemData.mods[modKey].id))?.id;
				}
			}

			Object.keys(itemData?.mods).forEach((key) => {
				const mod = itemData.mods[key];
				const modCalc = {};
				switch (mod.type) {
					case M5ModType.ATTRIBUTE: {
						for (const key of enumKeys(M5Attributes)) {
							const val: string = M5Attributes[key];
							modCalc[val] = (game as Game).i18n.localize(`midgard5.actor-${val}-long`);
						}
						break;
					}
					case M5ModType.STAT: {
						for (const key of enumKeys(M5Stats)) {
							const val: string = M5Stats[key];
							modCalc[val] = (game as Game).i18n.localize(`midgard5.mod-stat-${val}`);
						}
						break;
					}
					case M5ModType.SKILL: {
						if (character) {
							const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true, effects: true, kampfkuenste: true });
							if (actorCalc) {
								let category = (game as Game).i18n.localize("midgard5.skill");
								Object.keys(actorCalc.skills.general).forEach((skillId) => {
									const skill = character.getItem(skillId);
									if (skill) modCalc[skillId] = `${category}: ${skill.name}`;
								});

								category = (game as Game).i18n.localize("midgard5.language");
								Object.keys(actorCalc.skills.language).forEach((skillId) => {
									const skill = character.getItem(skillId);
									if (skill) modCalc[skillId] = `${category}: ${skill.name}`;
								});

								category = (game as Game).i18n.localize("midgard5.weapon-skill");
								Object.keys(actorCalc.skills.combat).forEach((skillId) => {
									const skill = character.getItem(skillId);
									if (skill) modCalc[skillId] = `${category}: ${skill.name}`;
								});

								category = (game as Game).i18n.localize("midgard5.innate-ability");
								Object.keys(actorCalc.skills.innate).forEach((skillId) => {
									const skill = character.getItem(skillId);
									if (skill) modCalc[skillId] = `${category}: ${skill.name}`;
								});
							}
						}
						break;
					}
				}

				calc.mods[key] = modCalc;
			});
		}
	}

	getRollData() {
		const actor = this.actor as any;
		const item = this as any;

		let ret: M5RollData = actor?.getRollData() ?? {
			c: null,
			i: null,
			iType: null,
			rolls: {},
			res: {},
		};

		ret.i = item.system;
		ret.iType = item.type;
		return ret;
	}

	async roll() {
		const item = this as any;

		// Initialize chat data.
		const speaker = ChatMessage.getSpeaker({ actor: this.actor });
		const rollMode = (game as Game).settings.get("core", "rollMode");
		const label = `[${item.type}] ${item.name}`;

		// If there's no roll data, send a chat message.
		const formulaNames = item.system.rolls?.formulas ? Object.keys(item.system.rolls.formulas) : [];
		if (formulaNames.length > 0) {
			const rollData = this.getRollData();
			formulaNames.forEach((formulaName) => {
				const formula = item.system.rolls.formulas[formulaName];
				if (formula) {
					rollData.rolls[formulaName] = {
						formula: formula.formula,
						label: formula.label,
						enabled: formula.enabled,
						result: "",
						total: 0,
						totalStr: "",
						dice: {},
					} as M5RollResult;
				}
			});

			if (item.type === "spell" || item.type === "kampfkunst") {
				if (this.actor["system"].ap.value >= item.system.ap) {
					this.actor["system"].ap.value -= item.system.ap;
				}
			}

			const roll = new M5Roll(rollData, this.actor, item.name);
			return roll.toMessage();
		} else {
			ChatMessage.create({
				speaker: speaker,
				rollMode: rollMode,
				flavor: label,
				content: item.system.description ?? "",
			});
			return null;
		}
	}

	getItem(itemId: string): any {
		return (this as any).getEmbeddedDocument("Item", itemId);
	}

	// migrateSystemData(): any {
	// 	const item = (this as any)
	// 	const data = item.system

	// 	if (item.type === "spell") {
	// 		if (typeof data.ap !== "string") {
	// 			data.ap = Number.isFinite(data.ap) ? "" + data.ap : ""
	// 		}
	// 	}

	// 	return super.migrateSystemData()
	// }
}
