#include <cstdint>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>

#include <uv.h>
#include <td/telegram/Client.h>
#include <td/telegram/td_api.h>
#include <td/telegram/td_api.hpp>

#include "common.h"

namespace td_api = td::td_api;
using TgObject = td_api::object_ptr<td_api::Object>;

class TelegramClient {
public:
  TelegramClient(uv_loop_t *eventLoop);
  ~TelegramClient();
  void add_update_handler(std::function<void(void*, td_api::Object&)> handler, void *context);
  void send_query(td_api::object_ptr<td_api::Function> f, std::function<void(TgObject)> callback);
  bool start();
private:
  static void uv_callback(uv_async_t *h);
  static void uv_close_handle_callback(uv_handle_t *h);
  void run_thread();
  void run_telegram_main_loop();
  bool process_response(td::ClientManager::Response response);
  void dispatch_response(std::uint64_t queryId, TgObject response);
  void dispatch_update(TgObject &update);

  bool m_running = false;
  bool m_authorized = false;
  std::uint64_t m_nextQueryId = 0;
  std::map<std::uint64_t, std::function<void(TgObject)>> m_handlers;
  std::vector<std::pair<std::function<void(void*, td_api::Object&)>, void*>> m_updateHandlers;
  std::thread m_thread;
  std::unique_ptr<td::ClientManager> m_clientManager;
  std::int32_t m_clientId;
  uv_loop_t *m_eventLoop;
  uv_async_t *m_uvHandle = nullptr;
  std::string m_phoneNumber;
  std::string m_password;
  std::deque<td::ClientManager::Response> m_responseQueue;
  std::mutex m_responseQueueMutex;
};
