//
// Created by sebastian on 02.08.26.
//

#ifndef COLORRACE_LUDOGAMEMODE_H
#define COLORRACE_LUDOGAMEMODE_H
#include <memory>
#include <optional>
#include <random>

#include "DiceSource.h"
#include "LudoGameState.h"
#include "LudoTypes.h"
#include "game/TurnBasedGameMode.h"

namespace ludo {
    struct PathNode {
        PieceState state;
        int position;

        friend bool operator==(const PathNode& lhs, const PathNode& rhs) {
            return lhs.state == rhs.state && lhs.position == rhs.position;
        }

        static inline void PrintTo(const PathNode& node, std::ostream* os) {
            *os << "PathNode{state=" << static_cast<int>(node.state)
                << ", position=" << node.position << "}";
        }
    };

    class LudoGameMode: public engine::TurnBasedGameMode<LudoCommand, LudoEvent, LudoGameState> {
    public:
        explicit LudoGameMode(std::vector<engine::PlayerID> players, std::optional<unsigned int> seed = std::nullopt) : TurnBasedGameMode(std::move(players)),
            m_diceSource(std::make_unique<RandomDiceSource>()), m_gameState(getPlayerOrder()) {}
        void sendCommand(const std::variant<MovePieceCommand, RollDiceCommand> &command) override;
        std::vector<LudoEvent> pollEvents() override;


        [[nodiscard]] const LudoGameState& getState() const override { return m_gameState; }
        [[nodiscard]] PlayerColor getColorOf(engine::PlayerID playerID) const;

        std::vector<int> getMoveablePieceIndices(PlayerColor color, int diceValue) const;
        std::optional<int> wouldCapture(int pieceIndex, int diceValue) const;
        [[nodiscard]] static int getEntryOffset(PlayerColor color);
        static std::vector<PathNode> computePath(PlayerColor color, PieceState startingState, int startPosition, PieceState targetState, int targetPosition);

        void setDiceSource(std::unique_ptr<IDiceSource> diceSource) { m_diceSource = std::move(diceSource); }
    private:
        struct MoveResult {
            int fromPosition = 0;
            int toPosition = 0;
            bool captured = false;
            int capturedPieceIndex = -1;
            PieceState targetState = PieceState::OnTrack;
            PieceState startState = PieceState::OnTrack;
        };

        struct Destination {
            PieceState state;
            int position;
        };

        std::optional<Destination> computeDestination(const Piece &piece, int diceValue) const;
        [[nodiscard]] std::optional<int> findLowestHomeSlotPiece(PlayerColor color) const;

        MoveResult movePieceInternal(int pieceIndex, int diceValue);
        void autoReleasePiece(int pieceIndex);

        void handle(const RollDiceCommand &command);

        void handle(const MovePieceCommand &command);
        void finishTurn();


        LudoGameState m_gameState;
        std::vector<LudoEvent> m_pendingEvents;

        int rollDice();
        std::unique_ptr<IDiceSource> m_diceSource;


        [[nodiscard]] bool isBlockedByOwnPiece(PlayerColor owner, PieceState state, int position) const;

    };
}


#endif //COLORRACE_LUDOGAMEMODE_H
