package cz.jzitnik.game.mobs.services.zombie;

import cz.jzitnik.game.annotations.EntityLogic;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.mobs.EntityLogicInterface;
import cz.jzitnik.game.mobs.EntityLogicProvider;
import cz.jzitnik.game.sprites.Zombie;
import lombok.extern.slf4j.Slf4j;

import java.awt.*;
import java.util.List;

@Slf4j
@EntityLogic("zombie")
public class ZombieMoveLogic implements EntityLogicInterface {
    @Override
    public void nextIteration(EntityLogicProvider.EntityLogicMobDTO entityLogicMobDTO) {
        int zombieX = entityLogicMobDTO.getX();
        int zombieY = entityLogicMobDTO.getY();
        var game = entityLogicMobDTO.getGame();
        var zombie = entityLogicMobDTO.getMob();

        // Skip updates for top half
        if (zombie.getSpriteState().get() == Zombie.ZombieState.TOP ||
                zombie.getSpriteState().get() == Zombie.ZombieState.TOP_HURT) {
            log.debug("Skipping top half of zombie at ({},{})", zombieX, zombieY);
            return;
        }

        log.debug("Zombie tick @ ({},{})", zombieX, zombieY);

        var world = game.getWorld();
        var zombieData = (ZombieData) zombie.getData();

        // Player coordinates
        Point playerCords = game.getPlayerCordsPoint();
        int playerX = playerCords.x;
        int playerY = playerCords.y;
        log.debug("Player at ({},{})", playerX, playerY);

        // Decide direction
        int direction = Integer.compare(playerX, zombieX);
        zombieData.setLastDirection(direction);
        log.debug("Zombie direction: {}", direction);

        if (direction == 0) {
            return;
        }

        boolean updated = false;
        int newZombieX = zombieX;
        int newZombieY = zombieY;

        // Check bounds
        if (zombieX + direction < 0 || zombieX + direction >= world[0].length) {
            log.debug("Zombie cannot move, out of bounds at X: {}", zombieX + direction);
        } else {
            // Blocks ahead
            List<Block> blocksAhead = world[zombieY][zombieX + direction];
            log.debug("Blocks ahead solid? {}", game.isSolid(blocksAhead));

            if (!game.isSolid(blocksAhead)) {
                log.debug("Path clear, moving to ({},{})", zombieX + direction, zombieY);
                moveZombie(zombie, zombieX, zombieY, zombieX + direction, zombieY, world);
                newZombieX = zombieX + direction;
                updated = true;
                zombieData.setJumpAttempts(0);
            } else {
                log.debug("Path blocked, checking jump options");
                List<Block> blocksAboveAhead = world[zombieY - 1][zombieX + direction];
                List<Block> blocksTwoAboveAhead = world[zombieY - 2][zombieX + direction];

                log.debug("Blocks above ahead solid? {}", game.isSolid(blocksAboveAhead));
                log.debug("Blocks two above ahead solid? {}", game.isSolid(blocksTwoAboveAhead));

                if (!game.isSolid(blocksAboveAhead) && game.isSolid(blocksAhead) && !game.isSolid(blocksTwoAboveAhead)) {
                    if (zombieData.getJumpAttempts() < 2) {
                        log.debug("Jumping to ({},{})", zombieX + direction, zombieY - 1);
                        moveZombie(zombie, zombieX, zombieY, zombieX + direction, zombieY - 1, world);
                        newZombieX = zombieX + direction;
                        newZombieY = zombieY - 1;
                        updated = true;
                        zombieData.setJumpAttempts(zombieData.getJumpAttempts() + 1);
                    } else {
                        log.debug("Max jump attempts reached: {}", zombieData.getJumpAttempts());
                    }
                } else {
                    log.debug("Cannot jump over obstacle");
                }
            }
        }

        // Apply gravity
        while (updated) {
            if (newZombieY + 1 >= world.length) {
                log.debug("Gravity blocked, bottom of world reached");
                updated = false;
                break;
            }

            if (!game.isSolid(world[newZombieY + 1][newZombieX])) {
                if (newZombieY - zombieY < 3) {
                    log.debug("Gravity moving zombie down from ({},{}) to ({},{})", newZombieX, newZombieY, newZombieX, newZombieY + 1);
                    moveZombie(zombie, newZombieX, newZombieY, newZombieX, newZombieY + 1, world);
                    newZombieY++;
                } else {
                    log.debug("Gravity limit reached, stopping");
                    updated = false;
                }
            } else {
                log.debug("Block below solid, stopping gravity");
                updated = false;
            }
        }
    }

    private void moveZombie(Block zombie, int oldX, int oldY, int newX, int newY, List<Block>[][] world) {
        Block linked = zombie.getLinkedMobTexture();

        boolean isTop = zombie.getSpriteState().isPresent() &&
                (zombie.getSpriteState().get().equals(Zombie.ZombieState.TOP) ||
                        zombie.getSpriteState().get().equals(Zombie.ZombieState.TOP_HURT));

        if (isTop) {
            log.debug("Moving TOP block from ({},{}) to ({},{})", oldX, oldY, newX, newY);
            world[oldY][oldX].remove(zombie);
            world[oldY + 1][oldX].remove(linked);

            world[newY][newX].add(zombie);
            world[newY + 1][newX].add(linked);
        } else {
            log.debug("Moving BOTTOM block from ({},{}) to ({},{})", oldX, oldY, newX, newY);
            world[oldY][oldX].remove(zombie);
            world[oldY - 1][oldX].remove(linked);

            world[newY][newX].add(zombie);
            world[newY - 1][newX].add(linked);
        }
    }
}
