package cz.jzitnik.game.logic.services.leaves;

import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.CustomLogic;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.logic.CustomLogicInterface;
import cz.jzitnik.tui.ScreenRenderer;

import java.util.*;

@CustomLogic
public class LeavesFallingLogic implements CustomLogicInterface {
    private static final int RADIUS = 30;
    private static final int SAFE_MARGIN = 5;
    private final Random random = new Random();

    @Override
    public void nextIteration(Game game, ScreenRenderer ignored) {
        int[] data = game.getPlayerCords();
        var world = game.getWorld();
        int playerX = data[0];
        int playerY = data[1];

        int startX = Math.max(0, playerX - RADIUS);
        int startY = Math.max(0, playerY - RADIUS);
        int endX = Math.min(world[0].length - 1, playerX + RADIUS);
        int endY = Math.min(world.length - 1, playerY + RADIUS);

        Set<String> supportedLeaves = findSupportedLeaves(world, startX, startY, endX, endY);
        List<int[]> fallingLeaves = new ArrayList<>();

        for (int x = startX; x <= endX; x++) {
            for (int y = startY; y <= endY; y++) {
                List<Block> blocks = world[y][x];
                if (blocks.stream().anyMatch(block -> block.getBlockId().equals("oak_leaves"))) {
                    String key = x + "," + y;
                    if (!supportedLeaves.contains(key)) {
                        fallingLeaves.add(new int[] { x, y });
                    }
                }
            }
        }

        if (!fallingLeaves.isEmpty() && random.nextInt(4) < 1) {
            List<int[]> safeFallingLeaves = fallingLeaves.stream()
                    .filter(leaf -> leaf[0] > startX + SAFE_MARGIN &&
                            leaf[0] < endX - SAFE_MARGIN &&
                            leaf[1] > startY + SAFE_MARGIN &&
                            leaf[1] < endY - SAFE_MARGIN)
                    .toList();

            if (!safeFallingLeaves.isEmpty()) {
                int[] leafToRemove = safeFallingLeaves.get(random.nextInt(safeFallingLeaves.size()));
                int leafX = leafToRemove[0];
                int leafY = leafToRemove[1];
                world[leafY][leafX].removeIf(block -> block.getBlockId().equals("oak_leaves"));
            }
        }
    }

    private Set<String> findSupportedLeaves(List<Block>[][] world, int startX, int startY, int endX, int endY) {
        Set<String> supportedLeaves = new HashSet<>();
        Queue<int[]> queue = new LinkedList<>();

        for (int x = startX; x <= endX; x++) {
            for (int y = startY; y <= endY; y++) {
                List<Block> blocks = world[y][x];
                if (blocks.stream().anyMatch(block -> block.getBlockId().equals("oak_log"))) {
                    queue.add(new int[] { x, y });
                }
            }
        }

        int[][] directions = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }, { -1, 1 }, { 1, 1 }, { -1, -1 }, { 1, -1 } };

        while (!queue.isEmpty()) {
            int[] pos = queue.poll();
            int cx = pos[0];
            int cy = pos[1];

            for (int[] dir : directions) {
                int nx = cx + dir[0];
                int ny = cy + dir[1];

                if (nx >= startX && nx <= endX && ny >= startY && ny <= endY) {
                    List<Block> neighborBlocks = world[ny][nx];
                    if (neighborBlocks.stream().anyMatch(block -> block.getBlockId().equals("oak_leaves"))) {
                        String key = nx + "," + ny;
                        if (!supportedLeaves.contains(key)) {
                            supportedLeaves.add(key);
                            queue.add(new int[] { nx, ny });
                        }
                    }
                }
            }
        }

        return supportedLeaves;
    }
}
