#pragma once

#include <cstdint>
#include <deque>
#include <mutex>
#include <optional>
#include <string>
#include <vector>

#include <nlohmann/json.hpp>

class Game501
{
public:
    struct DartValue
    {
        std::string notation;
        int value = 0;
        bool is_double = false;
    };

    Game501();

    nlohmann::json start(const std::vector<std::string> &player_names);
    nlohmann::json addDart(const std::string &notation, const std::string &source = "detector");
    nlohmann::json undo();
    nlohmann::json markBoardClear();
    nlohmann::json reset();
    nlohmann::json state() const;
    bool isPlaying() const;

    static std::optional<DartValue> parseDart(const std::string &notation);

private:
    struct Dart
    {
        std::string notation;
        int value = 0;
        bool is_double = false;
        bool bust = false;
        std::string source;
        std::int64_t timestamp = 0;
    };

    struct Player
    {
        std::string name;
        int score = 501;
    };

    struct Turn
    {
        int player_index = 0;
        int round = 1;
        int start_score = 501;
        int end_score = 501;
        std::string outcome;
        std::vector<Dart> darts;
    };

    struct Snapshot
    {
        std::string status;
        std::vector<Player> players;
        int active_player = 0;
        int round = 1;
        int turn_start_score = 501;
        std::vector<Dart> current_darts;
        std::deque<Turn> history;
        bool awaiting_clear = false;
        int winner = -1;
    };

    static std::string normalizeName(const std::string &name, std::size_t index);
    static nlohmann::json dartJson(const Dart &dart);
    static std::int64_t nowMillis();

    Snapshot snapshotLocked() const;
    void restoreLocked(const Snapshot &snapshot);
    void archiveTurnLocked(const std::string &outcome);
    void advancePlayerLocked();
    nlohmann::json stateLocked() const;
    nlohmann::json responseLocked(bool accepted, const std::string &message) const;

    mutable std::mutex mutex_;
    std::string status_ = "idle";
    std::vector<Player> players_;
    int active_player_ = 0;
    int round_ = 1;
    int turn_start_score_ = 501;
    std::vector<Dart> current_darts_;
    std::deque<Turn> history_;
    std::vector<Snapshot> undo_stack_;
    bool awaiting_clear_ = false;
    int winner_ = -1;
};

