#pragma once

#include <cstdint>
#include <functional>
#include <string>
#include <utility>

// --- Clase PauseManager: maneja el sistema de pausa del juego ---
class PauseManager {
   public:
    // --- Enums ---
    enum class Source : uint8_t {  // Fuentes de pausa
        NONE = 0,
        PLAYER = 1 << 0,        // 0001
        SERVICE_MENU = 1 << 1,  // 0010
        FOCUS_LOST = 1 << 2     // 0100
    };

    // --- Operadores friend ---
    friend auto operator|(Source a, Source b) -> Source {
        return static_cast<Source>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));  // NOLINT(readability-redundant-casting)
    }

    friend auto operator&(Source a, Source b) -> Source {
        return static_cast<Source>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b));  // NOLINT(readability-redundant-casting)
    }

    friend auto operator~(Source a) -> uint8_t {
        return ~static_cast<uint8_t>(a);
    }

    friend auto operator&=(Source& a, uint8_t b) -> Source& {
        a = static_cast<Source>(static_cast<uint8_t>(a) & b);
        return a;
    }

    friend auto operator|=(Source& a, Source b) -> Source& {
        return a = a | b;
    }

    friend auto operator&=(Source& a, Source b) -> Source& {
        return a = a & b;
    }

    // --- Constructor y destructor ---
    explicit PauseManager(std::function<void(bool)> callback = nullptr)  // Constructor con callback opcional
        : on_pause_changed_callback_(std::move(callback)) {}

    // --- Métodos principales ---
    void setFlag(Source source, bool enable) {  // Establece/quita una fuente de pausa específica
        bool was_paused = isPaused();

        if (enable) {
            flags_ |= source;
        } else {
            flags_ &= ~source;  // Ahora funciona: Source &= uint8_t
        }

        if (was_paused != isPaused()) {
            notifyPauseChanged();
        }
    }

    // --- Métodos específicos para cada fuente ---
    void setPlayerPause(bool enable) { setFlag(Source::PLAYER, enable); }
    void setServiceMenuPause(bool enable) { setFlag(Source::SERVICE_MENU, enable); }
    void setFocusLossPause(bool enable) { setFlag(Source::FOCUS_LOST, enable); }

    void togglePlayerPause() { setPlayerPause(!isPlayerPaused()); }  // Toggle para el jugador (más común)

    // --- Getters ---
    [[nodiscard]] auto isPaused() const -> bool { return flags_ != Source::NONE; }
    [[nodiscard]] auto isPlayerPaused() const -> bool { return hasFlag(Source::PLAYER); }
    [[nodiscard]] auto isServiceMenuPaused() const -> bool { return hasFlag(Source::SERVICE_MENU); }
    [[nodiscard]] auto isFocusLossPaused() const -> bool { return hasFlag(Source::FOCUS_LOST); }

    [[nodiscard]] auto getFlags() const -> Source { return flags_; }  // Obtiene las banderas actuales

    // --- Métodos de utilidad ---
    void clearAll() {  // Limpia todas las pausas (útil para reset)
        if (isPaused()) {
            flags_ = Source::NONE;
            notifyPauseChanged();
        }
    }
    [[nodiscard]] auto getStatusString() const -> std::string {  // Método para debug
        if (flags_ == Source::NONE) {
            return "Active";
        }

        std::string result = "Paused by: ";
        bool first = true;

        if (hasFlag(Source::PLAYER)) {
            if (!first) {
                result += ", ";
            }
            result += "Player";
            first = false;
        }
        if (hasFlag(Source::SERVICE_MENU)) {
            if (!first) {
                result += ", ";
            }
            result += "ServiceMenu";
            first = false;
        }
        if (hasFlag(Source::FOCUS_LOST)) {
            if (!first) {
                result += ", ";
            }
            result += "FocusLoss";
        }

        return result;
    }
    void setCallback(std::function<void(bool)> callback) {  // Permite cambiar el callback en runtime
        on_pause_changed_callback_ = std::move(callback);
    }

   private:
    // --- Variables ---
    Source flags_ = Source::NONE;
    std::function<void(bool)> on_pause_changed_callback_;

    // --- Métodos internos ---
    [[nodiscard]] auto hasFlag(Source flag) const -> bool {
        return (flags_ & flag) != Source::NONE;
    }
    void notifyPauseChanged() {
        if (on_pause_changed_callback_) {
            on_pause_changed_callback_(isPaused());
        }
    }
};