#pragma once

#include <optional>
#include <vector>

#include <QPoint>

#include "BeltDragPath.h"
#include "BuildingId.h"
#include "BuildingType.h"
#include "FactoryState.h"
#include "GameConfig.h"
#include "Rotation.h"

// Where a building may be placed, and what is already sitting on those tiles.
// Free functions over the factory state and the config — they read no other
// system state, so they do not belong to BuildingSystem.

// True if every body cell lies inside the world: 0 <= y < world.height_tiles and
// x >= the current asteroid left edge (REQ-BLD-PLACE-VALID). Terrain type is not
// checked — see isPlacementValid for the full rule.
bool bodyCellsWithinWorldBounds(const FactoryState& state, const GameConfig& config,
                                const std::vector<QPoint>& bodyCells, QPoint anchor);

// True if the placement satisfies REQ-BLD-PLACE-VALID terrain and world-bounds
// rules: every ship-dock (S) cell sits in space (x >= 0), every other body (A)
// cell sits on the asteroid (x < 0 and x >= the left edge), and every cell has
// 0 <= y < world.height_tiles. There is no right-side bound — space extends
// rightward. Tile occupancy is NOT checked here.
bool isPlacementValid(const FactoryState& state, const GameConfig& config,
                      BuildingType type, QPoint anchor, Rotation rotation);

// An existing building or site whose footprint a ghost exactly covers.
struct CoincidingBuilding
{
    BuildingId id;
    Rotation   rotation;
};

// The building or site whose footprint a ghost of the given type/anchor/rotation
// exactly covers: same type, same body cells, a single owner. Operational buildings
// and construction sites alike. Rotation is not part of the test -- the target's own
// facing is reported so each caller can apply its own rule to it.
std::optional<CoincidingBuilding> findCoincidingSameTypeBuilding(const FactoryState& state,
                                                                 const GameConfig& config,
                                                                 BuildingType type,
                                                                 QPoint anchor,
                                                                 Rotation rot);

// The building or site that a ghost of the given type/anchor/rotation would
// rotate in place rather than replace: same type, same body cells, one owner
// (REQ-BLD-ROTATE-IN-PLACE). Tunnels never qualify. Builder mode only; blueprint
// placement mode never rotates anything -- see resolveBlueprintGhost.
std::optional<BuildingId> findRotateInPlaceTarget(const FactoryState& state,
                                                  const GameConfig& config,
                                                  BuildingType type, QPoint anchor,
                                                  Rotation rot);

// True if placing here would actually do something: the terrain and bounds rules of
// isPlacementValid hold, and the body cells are either all free or occupied only by
// a building this placement would rotate in place. This is the question the ghost
// asks to colour itself and the click path asks before enqueuing a command
// (REQ-BLD-GHOST, REQ-BLD-PLACE-VALID, REQ-BLD-ROTATE-IN-PLACE).
bool canPlaceBuilding(const FactoryState& state, const GameConfig& config,
                      BuildingType type, QPoint anchor, Rotation rotation);

// What one ghost of a blueprint would do at its resolved tile.
enum class BlueprintGhostAction
{
    PlaceNew,           // free, valid cells: a new construction site, charged for
    CompatibleOverlap,  // the same building is already there: left untouched, free
                        // (REQ-UI-BLUEPRINT-OVERLAP)
    Transfer,           // hand this blueprint's settings to the building already there,
                        // free (REQ-UI-BLUEPRINT-TRANSFER)
    Invalid             // terrain, bounds, or an overlap that is neither of the above
};

struct BlueprintGhostResolved
{
    BlueprintGhostAction      action;
    std::optional<BuildingId> targetId;  // set for CompatibleOverlap and Transfer
    // Where the ghost belongs on screen. The queried anchor and rotation, except at a
    // hovered transfer target, where the ghost snaps onto the target so it shows what the
    // click will act on (REQ-UI-BLUEPRINT-TRANSFER).
    QPoint                    ghostAnchor;
    Rotation                  ghostRotation;
};

// Classifies one ghost of a blueprint against the current factory state
// (REQ-UI-BLUEPRINT-OVERLAP, REQ-UI-BLUEPRINT-TRANSFER).
//
// `hoverTile` is set only for a blueprint holding exactly one building, and is then the
// tile under the cursor. That gesture is a copying tool rather than a layout, so it finds
// its transfer target by hit-testing the cursor instead of by footprint coincidence --
// without which a Shipyard could never be targeted at a different facing, its 4x2
// footprint covering entirely different tiles once rotated. Pass nullopt for a
// constellation, whose ghosts are judged purely by where the blueprint puts them. The
// size is read from the blueprint as stored, before locked types are dropped, so the
// gesture does not change behavior as the player unlocks things.
//
// Shared by the ghost coloring and the click path so a preview cannot disagree with what
// the click then does -- the same reason resolveBeltDragPath is shared.
BlueprintGhostResolved resolveBlueprintGhost(const FactoryState& state,
                                             const GameConfig& config,
                                             BuildingType type, QPoint anchor,
                                             Rotation rotation,
                                             std::optional<QPoint> hoverTile);

// What a belt drag would do to one tile of its path (REQ-BLD-BELT-DRAG).
enum class BeltTileAction
{
    PlaceNew,       // empty, valid cell: a new belt, subject to affordability
    RotateInPlace,  // already a belt (or belt site): re-oriented, free
    Invalid         // occupied by something else, or invalid terrain
};

struct BeltDragResolved
{
    BeltTileAction            action;
    bool                      affordable;  // meaningful only for PlaceNew
    std::optional<BuildingId> rotateId;    // set only for RotateInPlace
};

// Classifies every tile of a belt drag path against the current factory state,
// spending `buildingBlocksStock` cumulatively across the PlaceNew tiles so a path
// longer than the player can afford is only partly buildable (REQ-BLD-BELT-DRAG).
// Shared so the previewed ghosts and the placement on release cannot disagree.
std::vector<BeltDragResolved> resolveBeltDragPath(const std::vector<BeltPathTile>& path,
                                                  const FactoryState& state,
                                                  const GameConfig& config,
                                                  int buildingBlocksStock);
