package ai.worlds.panesterra.ahserionsflight;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aionemu.commons.utils.Rnd;
import com.aionemu.gameserver.ai.AIName;
import com.aionemu.gameserver.configs.main.SiegeConfig;
import com.aionemu.gameserver.controllers.attack.AggroInfo;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.stats.calc.Stat2;
import com.aionemu.gameserver.model.stats.container.StatEnum;
import com.aionemu.gameserver.services.panesterra.PanesterraService;
import com.aionemu.gameserver.services.panesterra.ahserion.AhserionRaid;
import com.aionemu.gameserver.services.panesterra.ahserion.PanesterraFaction;
import com.aionemu.gameserver.services.panesterra.ahserion.PanesterraTeam;
import com.aionemu.gameserver.skillengine.SkillEngine;
import com.aionemu.gameserver.skillengine.model.Effect;
import com.aionemu.gameserver.skillengine.model.SkillTemplate;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.PositionUtil;
import com.aionemu.gameserver.world.WorldPosition;

import ai.AggressiveNpcAI;

/**
 * @author Yeats, Estrayl
 */
@AIName("ahserion")
public class AhserionAI extends AggressiveNpcAI {

	private static final Logger log = LoggerFactory.getLogger("SIEGE_LOG");

	public AhserionAI(Npc owner) {
		super(owner);
	}

	@Override
	public void modifyOwnerStat(Stat2 stat) {
		if (stat.getStat() == StatEnum.MAXHP)
			stat.setBaseRate(SiegeConfig.AHSERION_MAX_PLAYERS_PER_TEAM / 100f);
	}

	@Override
	public float modifyDamage(Creature attacker, float damage, Effect effect) {
		if (effect != null && effect.getSkillId() == 21583) // Artillery Blast
			return damage * (SiegeConfig.AHSERION_MAX_PLAYERS_PER_TEAM / 100f);
		return super.modifyDamage(attacker, damage, effect);
	}

	@Override
	public void onStartUseSkill(SkillTemplate st, int lv) {
		switch (st.getSkillId()) {
			case 21566 -> { // Thunder Crash
				if (lv == 55 && System.currentTimeMillis() - getOwner().getGameStats().getFightStartingTime() < 30000)
					PacketSendUtility.broadcastMessage(getOwner(), 1501157); // _01: Beritra thinks me unready. I shall prove myself in your slaughter!
			}
			case 21570 -> { // Death Shriek
				if (lv == 58)
					PacketSendUtility.broadcastMessage(getOwner(), 1501163); // _07: Not enough time… My Lords, come save me!
			}
			case 21571 -> { // Ereshkigal's Reign
				WorldPosition p = getPosition();
				spawn(297186, p.getX(), p.getY(), p.getZ() + 10, (byte) 0); // Ereshkigal's Voice
			}
			case 21573 -> { // Distortion Pulse
				if (lv == 58)
					PacketSendUtility.broadcastMessage(getOwner(), 1501162); // _06: Impudent pestilence! Be gone!
			}
			// Ide Destruction
			case 21574 -> PacketSendUtility.broadcastMessage(getOwner(), 1501164); // _08: I will return you to your beloved Aion's dust!
		}
	}

	@Override
	public void onEndUseSkill(SkillTemplate st, int lv) {
		switch (st.getSkillId()) {
			case 21564 -> { // Dragon Claw
				if (lv == 58)
					handleBaseAssault();
			}
			case 21567 -> { // Toxic Spew
				if (lv == 58)
					handleSupportSpawns();
			}
			case 21574 -> handleIdeDestruction(); // Ide Destruction
		}

		// Custom solution to resolve the retail add hate event (switch_target_by_attacker_indicator)
		if (lv == 57 || lv == 26) {
			addHateToRandomPlayer();
		}
	}

	@Override
	public void onEffectApplied(Effect effect) {
		if (effect.getSkillId() == 21571) // Ereshkigal's Reign
			PacketSendUtility.broadcastMessage(getOwner(), 1501159); // _03: My head… Nooooo! Get out of my head!
	}

	@Override
	public void onEffectEnd(Effect effect) {
		if (effect.getSkillId() == 21571) // Ereshkigal's Reign
			PacketSendUtility.broadcastMessage(getOwner(), 1501161); // _03: Ereshkigal…? No! Nooooooo-argh!
	}

	private void handleSupportSpawns() {
		spawn(297353, 475.530f, 536.809f, 675.989f, (byte) 75);
		spawn(297353, 532.324f, 545.949f, 675.746f, (byte) 75);
		spawn(297353, 541.742f, 491.245f, 675.462f, (byte) 75);
		spawn(297353, 487.297f, 479.381f, 678.300f, (byte) 75);
	}

	private void handleBaseAssault() {
		if (getOwner().getWorldId() == 400030000 && AhserionRaid.getInstance().isStarted()) {
			for (PanesterraFaction faction : PanesterraFaction.values()) {
				PanesterraTeam team = PanesterraService.getInstance().getTeam(faction);
				if (team != null && !team.isEliminated())
					AhserionRaid.getInstance().spawnStage(5, faction);
			}
		}
	}

	/**
	 * Retail: activate_skillarea 21575
	 */
	private void handleIdeDestruction() {
		getKnownList().getKnownPlayers().values().stream().filter(p -> !p.isDead() && PositionUtil.isInRange(p, 509.64f, 513.25f, 675.145f, 45))
			.forEach(p -> SkillEngine.getInstance().getSkill(getOwner(), 21575, 1, p).useWithoutPropSkill());
	}

	private void addHateToRandomPlayer() {
		List<AggroInfo> attackingPlayers = getAggroList().getList().stream().filter(ai -> ai.getAttacker() instanceof Player player && !player.isDead())
			.toList();
		AggroInfo aggroInfo = Rnd.get(attackingPlayers);
		if (aggroInfo != null)
			aggroInfo.addHate(100000);
	}

	@Override
	protected void handleDied() {
		if (getOwner().getWorldId() == 400030000 && AhserionRaid.getInstance().isStarted()) {
			Map<PanesterraFaction, Integer> panesterraDamage = new HashMap<>();

			// Only players can attack Ahserion on this map.
			for (AggroInfo ai : getOwner().getAggroList().getFinalDamageList(false)) {
				if (ai.getAttacker() instanceof Player) {
					PanesterraTeam team = PanesterraService.getInstance().getTeam((Player) ai.getAttacker());
					if (team != null && !team.isEliminated()) {
						PanesterraFaction faction = team.getFaction();
						panesterraDamage.merge(faction, ai.getDamage(), Integer::sum);
					}
				}
			}
			PanesterraFaction winner = findWinnerTeam(panesterraDamage);
			if (winner != null)
				AhserionRaid.getInstance().handleBossKilled(getOwner(), winner);
		}
		logMetrics();
		super.handleDied();
	}

	private PanesterraFaction findWinnerTeam(Map<PanesterraFaction, Integer> panesterraDamage) {
		PanesterraFaction winner = null;
		int maxDmg = 0;
		for (PanesterraFaction faction : PanesterraFaction.values()) {
			Integer dmg = panesterraDamage.get(faction);
			if (dmg != null && !PanesterraService.getInstance().getTeam(faction).isEliminated()) {
				if (dmg > maxDmg) {
					maxDmg = dmg;
					winner = faction;
				}
			}
		}
		return winner;
	}

	private void logMetrics() {
		long fullFightTime = (System.currentTimeMillis() - getOwner().getGameStats().getFightStartingTime()) / 1000;
		String damageDealt = getAggroList().getFinalDamageList(false).stream().sorted((Comparator.comparingInt(AggroInfo::getDamage).reversed()))
			.map(ai -> String.format("%s (ID: %d, Dmg: %d)", ai.getAttacker().getName(), ai.getAttacker().getObjectId(), ai.getDamage()))
			.collect(Collectors.joining(", "));

		log.info("[{}] {} (ID:{}) was killed in {}s. Damage List: {}", getPosition().getWorldMapInstance().getTemplate().getName(), getOwner().getName(),
			getNpcId(), fullFightTime, damageDealt);
	}
}
