/*
 * SoulFire
 * Copyright (C) 2026  AlexProgrammerDE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package com.soulfiremc.server.pathfinding.controller;

import com.soulfiremc.server.bot.BotConnection;
import com.soulfiremc.server.pathfinding.SFVec3i;
import com.soulfiremc.server.pathfinding.execution.PathExecutor;
import com.soulfiremc.server.pathfinding.goals.BreakBlockPosGoal;
import com.soulfiremc.server.pathfinding.goals.CompositeGoal;
import com.soulfiremc.server.pathfinding.graph.constraint.DelegatePathConstraint;
import com.soulfiremc.server.pathfinding.graph.constraint.PathConstraint;
import com.soulfiremc.server.pathfinding.graph.constraint.PathConstraintImpl;
import com.soulfiremc.server.util.SFBlockHelpers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
public final class ExcavateAreaController {
  private final Set<SFVec3i> blocksToMine;

  public static Set<SFVec3i> getRectangleFromTo(SFVec3i from, SFVec3i to) {
    var cube = new HashSet<SFVec3i>();
    for (var x = Math.min(from.x, to.x); x <= Math.max(from.x, to.x); x++) {
      for (var y = Math.min(from.y, to.y); y <= Math.max(from.y, to.y); y++) {
        for (var z = Math.min(from.z, to.z); z <= Math.max(from.z, to.z); z++) {
          cube.add(new SFVec3i(x, y, z));
        }
      }
    }

    return Set.copyOf(cube);
  }

  public static Set<SFVec3i> getSphereRadius(SFVec3i origin, int radius) {
    var sphere = new HashSet<SFVec3i>();
    for (var x = -radius; x <= radius; x++) {
      for (var y = -radius; y <= radius; y++) {
        for (var z = -radius; z <= radius; z++) {
          if (Math.sqrt(x * x + y * y + z * z) <= radius) {
            sphere.add(new SFVec3i(origin.x + x, origin.y + y, origin.z + z));
          }
        }
      }
    }

    return Set.copyOf(sphere);
  }

  private boolean someBlocksCanBeMined(BotConnection bot) {
    return blocksToMine.stream().anyMatch(blockPos -> {
      var blockState = bot.minecraft().level.getBlockState(blockPos.toBlockPos());
      return SFBlockHelpers.isCollisionShapeFullBlock(blockState) && SFBlockHelpers.isDiggable(blockState.getBlock());
    });
  }

  public void start(BotConnection bot) {
    while (someBlocksCanBeMined(bot)) {
      log.info("Searching for next block to excavate");

      try {
        PathExecutor.executePathfinding(bot, new CompositeGoal(blocksToMine.stream()
          .map(BreakBlockPosGoal::new)
          .collect(Collectors.toUnmodifiableSet())), new DelegatePathConstraint() {
          private final PathConstraint pathConstraint = new PathConstraintImpl(bot);

          @Override
          public boolean canPlaceBlock(SFVec3i pos) {
            return pathConstraint.canPlaceBlock(pos) && !blocksToMine.contains(pos);
          }

          @Override
          public PathConstraint delegate() {
            return pathConstraint;
          }
        }).get();
      } catch (Exception e) {
        log.error("Got exception while executing path, aborting", e);
        return;
      }
    }
  }
}
