#include "StringUtils.h"
#include "VWeb.h"

#include <random>
#include <sstream>

namespace VWeb {
#pragma region AUTH
PreMiddleWareReturn AuthWare::PreHandle(Request &request) {
  if (m_AuthFunction)
    return m_AuthFunction(request);
  return {};
}

#pragma endregion AUTH

#pragma region SESSION

static std::string GenerateSID() {
  static std::random_device dev;
  static std::mt19937 rng(dev());

  std::uniform_int_distribution<int> dist(0, 15);

  const char *v = "0123456789abcdef";
  const bool dashArray[] = {false, false, false, false, true, false,
                            true,  false, true,  false, true, false,
                            false, false, false, false};

  std::stringstream res;
  for (bool dash : dashArray) {
    if (dash)
      res << "-";
    res << v[dist(rng)];
    res << v[dist(rng)];
  }
  return res.str();
}

SessionManager::SessionManager() {
  m_GCThread = CreateRef<std::thread>([this]() {
    while (m_IsRunning) {
      if (m_Counter == 59) {
        GC();
        m_Counter = -1;
      }
      m_Counter++;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  });
}
SessionManager::~SessionManager() {
  m_IsRunning = false;
  m_GCThread->join();
}

PreMiddleWareReturn SessionManager::PreHandle(Request &request) {
  auto &cookies = request.CookieData;
  if (!cookies->Has("sid"))
    return {};
  auto &cookie = cookies->Get("sid");
  std::lock_guard<std::mutex> lck(m_Mutex);
  if (!m_Sessions.contains(cookie.Value))
    return {};
  auto &session = m_Sessions[cookie.Value];
  session->Update();
  request.SessionData = session;
  return {};
}

bool SessionManager::PostHandle(const Request &request, Response &response) {
  if (response.SessionData->Id.empty() &&
      response.SessionData->ContainsData()) {
    response.SessionData->Update();
    {
      std::lock_guard<std::mutex> lck(m_Mutex);
      response.SessionData->Id = GenerateSID();
      while (m_Sessions.contains(response.SessionData->Id))
        response.SessionData->Id = GenerateSID();
    }
    m_Sessions[response.SessionData->Id] = response.SessionData;
    auto &sidCookie = response.CookieData->Get("sid");
    sidCookie.HttpOnly = true;
    sidCookie.Secure = true;
    sidCookie.Value = response.SessionData->Id;
  }
  return true;
}

void SessionManager::GC() {
  std::lock_guard<std::mutex> lck(m_Mutex);
  std::vector<std::string_view> mark_as_delete;
  for (auto &itemToCollect : m_Sessions) {
    if (!itemToCollect.second->IsValid())
      mark_as_delete.push_back(itemToCollect.first);
  }

  for (auto &i : mark_as_delete)
    m_Sessions.erase(i.data());
}
#pragma endregion SESSION
#pragma region COOKIES
PreMiddleWareReturn CookieManager::PreHandle(Request &request) {
  auto &cookieHeaders = request.Header("Cookie");
  auto &cookies = request.CookieData;
  if (cookieHeaders.Size() > 0) {
    auto &values = cookieHeaders.Values();
    for (auto &rawCookie : cookieHeaders.Values()) {
      auto splitCookies = String::Split(rawCookie, ";");
      for (auto &cookie : splitCookies) {
        auto split = String::Split(cookie, "=");
        String::Trim(split[0]);
        String::Trim(split[1]);
        cookies->CreateOld(split[0], split[1]);
      }
    }
  }
  return {};
}
#pragma endregion COOKIES
} // namespace VWeb