export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
	stealthrock: {
		inherit: true,
		condition: {
			// this is a side condition
			onSideStart(side, source) {
				this.add('-sidestart', side, 'move: Stealth Rock');
			},
			onSwitchIn(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'stealthrock');
				if (pokemon.hasItem('heavydutyboots') || !calc) return;
				this.damage(calc * pokemon.maxhp / 8);
			},
		},
	},
	gmaxsteelsurge: {
		inherit: true,
		condition: {
			// this is a side condition
			onSideStart(side, source) {
				this.add('-sidestart', side, 'move: G-Max Steelsurge');
			},
			onSwitchIn(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'stealthrock');
				if (pokemon.hasItem('heavydutyboots') || !calc) return;
				this.damage(calc * pokemon.maxhp / 8);
			},
		},
	},
	spikes: {
		inherit: true,
		condition: {
			// this is a side condition
			onSideStart(side, source) {
				this.add('-sidestart', side, 'Spikes');
				this.effectState.layers = 1;
			},
			onSideRestart(side, source) {
				if (this.effectState.layers >= 3) return false;
				this.add('-sidestart', side, 'Spikes');
				this.effectState.layers++;
			},
			onSwitchIn(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'spikes');
				if (!calc || !pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
				const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4
				this.damage(calc * damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
			},
		},
	},
	axekick: {
		inherit: true,
		onMoveFail(target, source, move) {
			const calc = calculate(this, source, source, 'axekick');
			if (calc) this.damage(calc * source.baseMaxhp / 2, source, source, this.dex.conditions.get('High Jump Kick'));
		},
	},
	curse: {
		inherit: true,
		condition: {
			onStart(pokemon, source) {
				this.add('-start', pokemon, 'Curse', `[of] ${source}`);
			},
			onResidualOrder: 12,
			onResidual(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'curse');
				if (calc) this.damage(calc * pokemon.baseMaxhp / 4);
			},
		},
	},
	firepledge: {
		inherit: true,
		condition: {
			duration: 4,
			onSideStart(targetSide, source) {
				this.add('-sidestart', targetSide, 'Fire Pledge');
			},
			onResidualOrder: 5,
			onResidualSubOrder: 1,
			onResidual(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'firepledge');
				if (!pokemon.hasType('Fire') && calc) this.damage(calc * pokemon.baseMaxhp / 8, pokemon);
			},
			onSideResidualOrder: 26,
			onSideResidualSubOrder: 8,
			onSideEnd(targetSide) {
				this.add('-sideend', targetSide, 'Fire Pledge');
			},
		},
	},
	flameburst: {
		inherit: true,
		onHit(target, source, move) {
			for (const ally of target.adjacentAllies()) {
				const calc = calculate(this, source, ally, 'flameburst');
				if (calc) this.damage(calc * ally.baseMaxhp / 16, ally, source, this.dex.conditions.get('Flame Burst'));
			}
		},
		onAfterSubDamage(damage, target, source, move) {
			for (const ally of target.adjacentAllies()) {
				const calc = calculate(this, source, ally, 'flameburst');
				if (calc) this.damage(calc * ally.baseMaxhp / 16, ally, source, this.dex.conditions.get('Flame Burst'));
			}
		},
	},
	highjumpkick: {
		inherit: true,
		onMoveFail(target, source, move) {
			const calc = calculate(this, source, source, 'highjumpkick');
			if (calc) this.damage(calc * source.baseMaxhp / 2, source, source, this.dex.conditions.get('High Jump Kick'));
		},
	},
	jumpkick: {
		inherit: true,
		onMoveFail(target, source, move) {
			const calc = calculate(this, source, source, 'jumpkick');
			if (calc) this.damage(calc * source.baseMaxhp / 2, source, source, this.dex.conditions.get('Jump Kick'));
		},
	},
	leechseed: {
		inherit: true,
		condition: {
			onStart(target, source) {
				this.add('-start', target, 'move: Leech Seed');
			},
			onResidualOrder: 8,
			onResidual(pokemon) {
				const target = this.getAtSlot(pokemon.volatiles['leechseed'].sourceSlot);
				if (!target || target.fainted || target.hp <= 0) {
					this.debug('Nothing to leech into');
					return;
				}
				const calc = calculate(this, this.effectState.source, pokemon, 'leechseed');
				const damage = this.damage(calc * pokemon.baseMaxhp / 8, pokemon, target);
				if (damage) {
					this.heal(damage, target, pokemon);
				}
			},
		},
	},
	mindblown: {
		inherit: true,
		onAfterMove(pokemon, target, move) {
			if (move.mindBlownRecoil && !move.multihit) {
				const hpBeforeRecoil = pokemon.hp;
				const calc = calculate(this, pokemon, pokemon, 'mindblown');
				this.damage(Math.round(calc * pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get('Mind Blown'), true);
				if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
					this.runEvent('EmergencyExit', pokemon, pokemon);
				}
			}
		},
	},
	nightmare: {
		inherit: true,
		condition: {
			noCopy: true,
			onStart(pokemon, source) {
				if (pokemon.status !== 'slp' && !pokemon.hasAbility('comatose')) {
					return false;
				}
				this.add('-start', pokemon, 'Nightmare');
				this.effectState.source = source;
			},
			onResidualOrder: 11,
			onResidual(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'nightmare');
				if (calc) this.damage(calc * pokemon.baseMaxhp / 4);
			},
		},
	},
	powder: {
		inherit: true,
		condition: {
			duration: 1,
			onStart(target, source) {
				this.add('-singleturn', target, 'Powder');
			},
			onTryMovePriority: -1,
			onTryMove(pokemon, target, move) {
				if (move.type === 'Fire') {
					this.add('-activate', pokemon, 'move: Powder');
					const calc = calculate(this, this.effectState.source, pokemon, 'powder');
					if (calc) this.damage(this.clampIntRange(Math.round(calc * pokemon.maxhp / 4), 1));
					this.attrLastMove('[still]');
					return false;
				}
			},
		},
	},
	saltcure: {
		inherit: true,
		condition: {
			noCopy: true,
			onStart(pokemon, source) {
				this.add('-start', pokemon, 'Salt Cure');
				this.effectState.source = source;
			},
			onResidualOrder: 13,
			onResidual(pokemon) {
				const calc = calculate(this, this.effectState.source, pokemon, 'saltcure');
				if (calc) this.damage(calc * pokemon.baseMaxhp / (pokemon.hasType(['Water', 'Steel']) ? 4 : 8));
			},
			onEnd(pokemon) {
				this.add('-end', pokemon, 'Salt Cure');
			},
		},
	},
	spikyshield: {
		inherit: true,
		condition: {
			duration: 1,
			onStart(target) {
				this.add('-singleturn', target, 'move: Protect');
			},
			onTryHitPriority: 3,
			onTryHit(target, source, move) {
				if (!move.flags['protect']) {
					if (['gmaxoneblow', 'gmaxrapidflow'].includes(move.id)) return;
					if (move.isZ || move.isMax) target.getMoveHitData(move).zBrokeProtect = true;
					return;
				}
				if (move.smartTarget) {
					move.smartTarget = false;
				} else {
					this.add('-activate', target, 'move: Protect');
				}
				const lockedmove = source.getVolatile('lockedmove');
				if (lockedmove) {
					// Outrage counter is reset
					if (source.volatiles['lockedmove'].duration === 2) {
						delete source.volatiles['lockedmove'];
					}
				}
				const calc = calculate(this, target, source, 'spikyshield');
				if (this.checkMoveMakesContact(move, source, target) && calc) {
					this.damage(calc * source.baseMaxhp / 8, source, target);
				}
				return this.NOT_FAIL;
			},
			onHit(target, source, move) {
				const calc = calculate(this, target, source, 'spikyshield');
				if (calc && move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
					this.damage(calc * source.baseMaxhp / 8, source, target);
				}
			},
		},
	},
	steelbeam: {
		inherit: true,
		onAfterMove(pokemon, target, move) {
			if (move.mindBlownRecoil && !move.multihit) {
				const hpBeforeRecoil = pokemon.hp;
				const calc = calculate(this, pokemon, pokemon, 'steelbeam');
				this.damage(Math.round(calc * pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get('Steel Beam'), true);
				if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
					this.runEvent('EmergencyExit', pokemon, pokemon);
				}
			}
		},
	},
	supercellslam: {
		inherit: true,
		onMoveFail(target, source, move) {
			const calc = calculate(this, source, source, 'supercellslam');
			if (calc) this.damage(calc * source.baseMaxhp / 2, source, source, this.dex.conditions.get('Supercell Slam'));
		},
	},
	toxicspikes: {
		inherit: true,
		condition: {
			// this is a side condition
			onSideStart(side) {
				this.add('-sidestart', side, 'move: Toxic Spikes');
				this.effectState.layers = 1;
			},
			onSideRestart(side) {
				if (this.effectState.layers >= 2) return false;
				this.add('-sidestart', side, 'move: Toxic Spikes');
				this.effectState.layers++;
			},
			onSwitchIn(pokemon) {
				if (!pokemon.isGrounded()) return;
				if (pokemon.hasType('Poison')) {
					this.add('-sideend', pokemon.side, 'move: Toxic Spikes', `[of] ${pokemon}`);
					pokemon.side.removeSideCondition('toxicspikes');
				} else if (pokemon.hasType('Steel') || pokemon.hasItem('heavydutyboots')) {
					// do nothing
				} else if (this.effectState.layers >= 2) {
					pokemon.trySetStatus('tox', this.effectState.source);
				} else {
					pokemon.trySetStatus('psn', this.effectState.source);
				}
			},
		},
	},
};

function calculate(battle: Battle, source: Pokemon, pokemon: Pokemon, moveid = 'tackle') {
	const move = battle.dex.getActiveMove(moveid);
	move.type = source.getTypes()[0];
	const typeMod = 2 ** battle.clampIntRange(pokemon.runEffectiveness(move), -6, 6);
	if (!pokemon.runImmunity(move.type)) return 0;
	return typeMod;
}
