#pragma once

#include <unordered_set>
#include <unordered_map>
#include <utility>
#include <string>

#include <memory>

#include <mutex>

#include <glaze/glaze.hpp>

#define WS_ADDR "0.0.0.0"
#define WS_PORT 4444

#define PLACEHOLDER_ITEM_ID "Anniversary2025Offering"

class TinyMITMProxy;

namespace ix
{
    class WebSocket;
    class WebSocketServer;
    struct WebSocketMessage;

    class ConnectionState;
} // namespace ix

namespace seallib
{
    class Logger;
}

struct SpooferConfig
{
        std::unordered_map<std::string, int> camperItems;
        std::unordered_map<std::string, int> camperAddons;
        std::unordered_map<std::string, int> slasherAddons;
        std::unordered_map<std::string, int> camperOfferings;
        std::unordered_map<std::string, int> slasherOfferings;
        std::unordered_map<std::string, int> globalOfferings;
        std::unordered_set<std::string> camperPerks;
        std::unordered_set<std::string> slasherPerks;
        std::unordered_set<std::string> catalogItemIds;
        std::unordered_set<std::string> dlcListGRDK;
        std::unordered_set<std::string> dlcListEGS;
        std::unordered_set<std::string> dlcListSteam;
        std::unordered_set<std::string> unlockedCharacters;
};

namespace WSMessages
{
    enum Action : int
    {
        INIT_CONFIG = 0,
        SYNC_STATE = 1,
        SYNC_TOGGLES = 2
    };
    struct Toggles
    {
            bool spoofItems = false;
            bool spoofPerks = false;
            bool spoofCatalog = false;
            bool spoofDLCs = false;
    };
    struct Init
    {
            Action action = INIT_CONFIG;
            SpooferConfig profile;
            Toggles toggles;
    };
    struct Request
    {
            Action action;
            SpooferConfig profile{};
            Toggles toggles{};
    };
} // namespace WSMessages

class Spoofer
{
    public:
        Spoofer();
        ~Spoofer();

        void registerListeners(TinyMITMProxy* proxy);

    private:
        /*
            local config handlers
        */
        void loadConfig();
        void saveConfig();

        /*
            websocket server stuff
        */
        void initServer();
        void stopServer();

        void wsMessageCallback(std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket& webSocket,
                               const std::unique_ptr<ix::WebSocketMessage>& msg);

        /*
            helpers
        */
        void modifyCharacterInventory(glz::generic& js);
        void modifyCharacterData(glz::generic& js);
        void generateBloodweb(glz::generic& js);

        /*
            api handlers
        */
        void onServerGetAll(std::string& body);
        void onServerInventoryAll(std::string& body);
        void onServerMessageList(std::string& body);
        void onServerBloodweb(std::string& body, std::string& respHeaders);
        void onServerUpdateEntitlements(const std::string& url, std::string& body);

        void onClientGetAll(std::string& body);
        void onClientBloodweb(std::string& body);
        void onClientUpdateEntitlements(const std::string& url, std::string& body);

        /*
            proxy handlers
        */
        void serverResponseHandler(const std::string& url, std::string& body, std::string& headers,
                                   bool& blockOutgoing);
        void clientRequestHandler(const std::string& url, std::string& body, std::string& headers, bool wasBlocked);

        /*
            config
        */
        bool _spoofItems = false;
        bool _spoofPerks = false;
        bool _spoofCatalog = false;
        bool _spoofDLCs = false;
        bool _spoofCharacters = false;

        std::unordered_map<std::string, int> _camperItems;
        std::unordered_map<std::string, int> _camperAddons;
        std::unordered_map<std::string, int> _slasherAddons;
        std::unordered_map<std::string, int> _camperOfferings;
        std::unordered_map<std::string, int> _slasherOfferings;
        std::unordered_map<std::string, int> _globalOfferings;

        std::unordered_set<std::string> _camperPerks;
        std::unordered_set<std::string> _slasherPerks;

        std::unordered_set<std::string> _catalogItemIds;

        std::unordered_set<std::string> _dlcListGRDK;
        std::unordered_set<std::string> _dlcListEGS;
        std::unordered_set<std::string> _dlcListSteam;

        std::unordered_set<std::string> _unlockedCharacters;

        /*
            internal variables
        */
        ix::WebSocketServer* _wsServer = nullptr;
        seallib::Logger* _log = nullptr;
        std::mutex _mutex;
        std::string _lastBwCharacter;
};
