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

#ifndef COLORRACE_LUDOTYPES_H
#define COLORRACE_LUDOTYPES_H
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <variant>

#include "game/TurnBasedGameMode.h"
#include <string_view>

namespace ludo {
    inline constexpr int kTrackLength = 40;
    inline constexpr int kHomeStretchLength = 4;
    inline constexpr int kHomePiecesPerPlayer = 4;
    inline constexpr float kPieceRadius = 0.7f;

    enum class PlayerColor : uint8_t { Red, Blue, Yellow, Green, Count};
    constexpr std::string_view toString(PlayerColor color)
    {
        switch(color)
        {
            case PlayerColor::Blue:
                return "Blue";

            case PlayerColor::Green:
                return "Green";

            case PlayerColor::Red:
                return "Red";

            case PlayerColor::Yellow:
                return "Yellow";
            case PlayerColor::Count:
                break;
        }

        return "Unknown";
    }

    constexpr std::array<PlayerColor, static_cast<size_t>(PlayerColor::Count)> PlayerColors = {
        PlayerColor::Red,
        PlayerColor::Blue,
        PlayerColor::Yellow,
        PlayerColor::Green
    };
    static PlayerColor getColorOf(engine::PlayerID player, std::vector<engine::PlayerID> playerOrder) {
        auto it = std::find(playerOrder.begin(), playerOrder.end(), player);
        assert(it != playerOrder.end() && "getColorOf mit unregistrierter PlayerID aufgerufen");
        return static_cast<PlayerColor>(std::distance(playerOrder.begin(), it));
    }

    enum class PieceState : uint8_t { AtHome, OnTrack, InHomeStretch, Finished };
    constexpr std::string_view toString(PieceState state)
    {
        switch(state)
        {
            case PieceState::AtHome:
                return "AtHome";
            case PieceState::OnTrack:
                return "OnTrack";
            case PieceState::InHomeStretch:
                return "InHomeStretch";
            case PieceState::Finished:
                return "Finished";
        }

        return "Unknown";
    }
    enum class TurnPhase : uint8_t { AwaitingRoll, AwaitingPieceSelection };

    struct Piece {
        PlayerColor color;
        PieceState state = PieceState::AtHome;
        int position = -1;
    };

    // Commands: Client -> Autorität, adressiert über PlayerID (Engine-Identität, netzwerkfähig)
    struct MovePieceCommand { engine::PlayerID player; int pieceIndex;};
    struct RollDiceCommand { engine::PlayerID player; };
    using LudoCommand = std::variant<MovePieceCommand, RollDiceCommand>;

    struct PieceMovedEvent {
        int pieceIndex;
        int fromPosition;
        int toPosition;
        bool captured;
        PieceState targetState;
        PieceState startState;

        static PieceMovedEvent create(int pieceIndex, int fromPosition, int toPosition,
                                   bool captured, PieceState startState, PieceState targetState) {
            validate(startState, fromPosition, targetState, toPosition, captured);
            return PieceMovedEvent{pieceIndex, fromPosition, toPosition, captured, targetState, startState};
        }

    private:
        PieceMovedEvent(
        int pieceIndex,
        int fromPosition,
        int toPosition,
        bool captured,
        PieceState targetState,
        PieceState startState)
        :
        pieceIndex(pieceIndex),
        fromPosition(fromPosition),
        toPosition(toPosition),
        captured(captured),
        targetState(targetState),
        startState(startState)
        {}
        static void validate(PieceState startState, int fromPosition,
                          PieceState targetState, int toPosition, bool captured) {
            if (captured && targetState != PieceState::AtHome) {
                throw std::invalid_argument(
                    "PieceMovedEvent: captured==true erwartet targetState==AtHome");
            }
            if (!captured && targetState == PieceState::AtHome && startState != PieceState::AtHome) {
                throw std::invalid_argument(
                    "PieceMovedEvent: targetState==AtHome ohne captured-Flag ist nicht vorgesehen");
            }
            // weitere Regeln nach Bedarf, z.B. Bereichschecks für fromPosition/toPosition
        }
    };
    struct DiceRolledEvent { engine::PlayerID player; int diceValue; };
    struct TurnChangedEvent {engine::PlayerID newPlayer; };
    using LudoEvent = std::variant<PieceMovedEvent, DiceRolledEvent, TurnChangedEvent>;


}
#endif //COLORRACE_LUDOTYPES_H
