#pragma once
#include <obs.hpp>
#include <websocketpp/client.hpp>
#include <QObject>
#include <mutex>
#include <condition_variable>
#include <set>

#ifdef USE_TWITCH_CLI_MOCK
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#else
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/config/asio.hpp>
#endif

namespace advss {

#ifdef USE_TWITCH_CLI_MOCK
typedef websocketpp::client<websocketpp::config::asio_client> EventSubWSClient;
#else
typedef websocketpp::client<websocketpp::config::asio_tls_client>
	EventSubWSClient;
#endif

using websocketpp::connection_hdl;
class TwitchToken;

struct Event {
	std::string id;
	std::string type;
	OBSData data;

	std::string ToString() const;
};

struct Subscription {
	OBSData data;
	std::string id;
	bool operator<(const Subscription &) const;
};

class EventSub : public QObject {
public:
	explicit EventSub();
	virtual ~EventSub();

	void Connect();
	void Disconnect();
	std::vector<Event> Events();
	bool SubscriptionIsActive(const std::string &id);
	static std::string AddEventSubscribtion(std::shared_ptr<TwitchToken>,
						Subscription);
	static void ClearAllEvents();
	void ClearActiveSubscriptions();

private:
	void OnOpen(connection_hdl hdl);
	void OnMessage(connection_hdl hdl,
		       EventSubWSClient::message_ptr message);
	void OnClose(connection_hdl hdl);
	void OnFail(connection_hdl hdl);
	void ConnectThread();

	bool IsValidMessageID(const std::string &);
	bool IsValidID(const std::string &);

	void HandleWelcome(obs_data_t *);
	void HandleKeepAlive() const;
	void HandleNotification(obs_data_t *);
	void HandleReconnect(obs_data_t *);
	void HanldeRevocation(obs_data_t *);

	void ClearEvents();

	void RegisterInstance();
	void UnregisterInstance();

	EventSubWSClient _client;
	connection_hdl _connection;
	std::thread _thread;
	std::mutex _waitMtx;
	std::mutex _connectMtx;
	std::condition_variable _cv;
	std::atomic_bool _connected{false};
	std::atomic_bool _disconnect{false};
	std::string _url;
	std::string _sessionID;

	std::mutex _messageMtx;
	std::vector<Event> _messages;
	std::deque<std::string> _messageIDs;
	std::mutex _subscriptionMtx;
	std::set<Subscription> _activeSubscriptions;
	static std::mutex _instancesMtx;
	static std::vector<EventSub *> _instances;
	static bool _setupDone;
};

} // namespace advss
