#include "StringUtils.h"

#include <VWeb.h>

namespace VWeb {

HttpMethod StringToHTTPMethod(std::string &method) {
  static std::unordered_map<std::string, HttpMethod> s_StringToMethodMap{
      {"get", HttpMethod::GET},         {"head", HttpMethod::HEAD},
      {"options", HttpMethod::OPTIONS}, {"post", HttpMethod::POST},
      {"put", HttpMethod::PUT},         {"patch", HttpMethod::PATCH},
      {"delete", HttpMethod::DELETE},   {"fallback", HttpMethod::FALLBACK},
  };
  String::ToLowerCase(method);
  if (s_StringToMethodMap.contains(method))
    return s_StringToMethodMap[method];
  return s_StringToMethodMap["fallback"];
}

bool ParseRequest(Ref<Request> &request) {
  std::istringstream resp(request->Body);
  std::string line;
  std::string::size_type index;

  while (std::getline(resp, line) && line != "\r") {
    index = line.find(':', 0);
    if (index != std::string::npos) {
      auto key = line.substr(0, index);
      String::Trim(key);
      request->Header(key).Add(String::TrimCopy(line.substr(index + 1)));
    } else if (line.find("HTTP")) {
      auto headers = String::Split(line, " ");
      if (headers.size() != 3)
        return false;
      request->Method = StringToHTTPMethod(headers[0]);
      request->URI = String::UrlDecode(headers[1]);
    } else {
      return false;
    }
  }
  return true;
}

struct RequestJob : public WorkerJob {
  Ref<Request> MRequest{};
  Ref<Router> MRouter{};
  Ref<MiddleWareHandler> MMiddleWareHandler{};
  RequestHandler *MRequestHandler{nullptr};

  void Execute() override {
    if (!ParseRequest(MRequest)) {
      fprintf(stderr, "[Request] >> Request failed to parse\n");
      SocketUtils::Close(MRequest->ID);
      return;
    }
    MRequest->CookieData = CreateRef<Cookies>();
    MRequest->SessionData = CreateRef<Session>();
    MMiddleWareHandler->HandlePre(MRequest);
    auto response = MRouter->HandleRoute(MRequest);
    MMiddleWareHandler->HandlePost(MRequest, response);
    auto content = response->GetResponse();
    MRequestHandler->AddSendResponse(
        {0, (ssize_t)content.size(), MRequest->ID, content});
    MMiddleWareHandler->Shutdown(MRequest, response);
  }
};
RequestHandler::RequestHandler(const Ref<SocketManager> &manager)
    : m_SocketManager(manager), m_MiddleWare(CreateRef<MiddleWareHandler>()) {
  m_MiddleWare = CreateRef<MiddleWareHandler>();
}
void RequestHandler::InitThreads(int count) {
  m_Pool.SetThreadCount(count);
  m_Pool.Create();
}
void RequestHandler::AddRequest(Ref<Request> &request) {
  if (m_Router) {
    auto job = CreateRef<RequestJob>();
    job->MRequest = request;
    job->MMiddleWareHandler = m_MiddleWare;
    job->MRequestHandler = this;
    job->MRouter = m_Router;
    m_Pool.Dispatch(job);
  }
}
void RequestHandler::Stop() {
  m_Pool.Stop();
}
void RequestHandler::SetRouter(Ref<Router> &router) {
  m_Router = router;
}
void RequestHandler::AddSendResponse(SendData sendData) {
  auto id = sendData.SocketID;
  m_Server->m_OutRequest.Add(id, sendData);
  if (!m_SocketManager->SetSendListen(id)) {
    SocketUtils::Close(id);
    m_Server->m_OutRequest.Remove(id);
  }
}
} // namespace VWeb