#include "httplib.h"
#include <iostream>
#include <mutex>
#include <source_location>
#include <string_view>
#include <vector>
#ifndef DEBUG_MODE
#define DEBUG_MODE false
#endif

// Autor: Paweł Kutyła


// Klasa Logger wykorzystuje std::source_location do logowania wiadomości z
// informacjami o lokalizacji kodu, co pozwala na skuteczne debugowanie w
// przypadku błędów. Aby włączyć tryb debugowania, należy stworzyć folder build,
// a następnie skopilować program przy użyciu komendy:
// cmake -DDEBUG_MODE=1 ..
// W trybie bez debugowania (RELEASE) kompilowany jest program bez logów przy
// użyciu komendy: cmake -DDEBUG_MODE=0 ..
class Logger {
public:
  static void
  log(const std::string_view message,
      const std::source_location location = std::source_location::current()) {
    if (DEBUG_MODE) { // Sprawdza, czy tryb debugowania jest włączony, jeśli tak
                      // - loguje metadane lokalizacji i wiadomosć
      std::cout << "Log: " << message << "\n"
                << "  File: " << location.file_name() << "\n"
                << "  Function: " << location.function_name() << "\n"
                << "  Line: " << location.line() << "\n"
                << "  Column: " << location.column() << std::endl;
    }
  }
};

// Globalne zmienne do przechowywania zarejestrowanych użytkowników oraz mutex
// do zapewnienia bezpieczeństwa wątków. Rozwiązanie to ma charakter tylko
// przykładowy i ma na celu zaprezentować działanie sourceLocation do celów
// debugowania.
std::vector<std::string> users;
std::mutex users_mutex;

// Handler żądań HTTP dla logowania
void logHandler(const httplib::Request &req, httplib::Response &res) {
  // Loguje otrzymanie żądania podczas logowania
  Logger::log("Received request to /log", std::source_location::current());
  res.set_content("Logging endpoint reached",
                  "text/plain"); // Ustawia treść odpowiedzi HTTP
}

// Handler żądań HTTP do rejestrowania nowego użytkownika
void registerHandler(const httplib::Request &req, httplib::Response &res) {
  auto user =
      req.get_param_value("user"); // Pobiera nazwę użytkownika z requesta HTTP
  {
    std::lock_guard<std::mutex> lock(
        users_mutex); // Blokuje mutex, aby zapewnić bezpieczeństwo wątków
                      // podczas modyfikacji users
    users.push_back(
        user); // Dodaje użytkownika do listy zarejestrowanych użytkowników
  }
  // Loguje po zakończeniu rejestracji użytkownika
  Logger::log("User registered: " + user, std::source_location::current());
  res.set_content("User " + user + " registered",
                  "text/plain"); // Ustawia treść odpowiedzi HTTP
}

// Handler żądań HTTP do wyświetlania listy zarejestrowanych użytkowników
void listUsersHandler(const httplib::Request &req, httplib::Response &res) {
  // Loguje otrzymanie żądania do wyświetlenia listy użytkowników
  Logger::log("Received request to /users", std::source_location::current());
  std::string user_list = "Registered users:\n";
  {
    std::lock_guard<std::mutex> lock(
        users_mutex); // Blokuje mutex, aby zapewnić bezpieczeństwo wątków
                      // podczas odczytu users
    // Iteruje przez listę zarejestrowanych użytkowników i dodaje ich nazwy do
    // listy użytkowników, która później zostanie wyświetlona w ramach
    // odpowiedzi HTTP
    for (const auto &user : users) {
      user_list += user + "\n";
    }
  }
  res.set_content(user_list, "text/plain"); // Ustawia treść odpowiedzi HTTP
}

// Główna funkcja ustawiająca serwer i końcówki
int main() {
  httplib::Server svr; // Tworzy instancję serwera HTTP
  // Ustawianie endpointów HTTP GET i handlerów do logowania
  svr.Get("/log", logHandler);
  svr.Get("/register", registerHandler);
  svr.Get("/users", listUsersHandler);
  // Loguje rozpoczęcie działania serwera
  Logger::log("Starting server on port 8080", std::source_location::current());
  svr.listen("0.0.0.0", 8080); // Uruchamia serwer na porcie 8080
  // Loguje zatrzymanie serwera
  Logger::log("Server stopped", std::source_location::current());

  return 0;
}

/*
 * Dokumentacja i odnośniki:
 *
 * 1. `httplib.h` - Biblioteka HTTP dla C++
 *    - GitHub: https://github.com/yhirose/cpp-httplib
 *    - Docs: https://github.com/yhirose/cpp-httplib#readme
 *
 * 2. `std::source_location` - Funkcja C++20 do rejestrowania lokalizacji kodu
 * źródłowego
 *    - cppreference: https://en.cppreference.com/w/cpp/utility/source_location
 *    - Przykład: https://www.geeksforgeeks.org/std_source_location-in-cpp-20/
 *
 *
 * Aplikacja demonstruje użycie funkcji C++ dostępnych w ramach c++ 20
 * (sourceLocation) i bibliotek, w tym prostego serwera HTTP, rejestrowania
 * użytkownik oraz logowania z
 * szczegółowymi informacjami o lokalizacji kodu.
 * Przykładowy log:
 * Function: int main()
 * Line: 93
 * Column: 47
 * Log: User registered: Bob
 * File: /Users/pkutyl/ZPRDOD/sourceLocation.cpp
 * Function: void registerHandler(const httplib::Request &, httplib::Response &)
 * Line: 61
 * Column: 43
 * Log: Received request to /users
 * File: /Users/pkutyl/ZPRDOD/sourceLocation.cpp
 * Function: void listUsersHandler(const httplib::Request &, httplib::Response
 * &) Line: 69 Column: 45 Log: Received request to /log File:
 * /Users/pkutyl/ZPRDOD/sourceLocation.cpp Function: void logHandler(const
 * httplib::Request &, httplib::Response &) Line: 44 Column: 43
 * Przykladowe zapytania:
 * curl http://localhost:8080/log
 * curl "http://localhost:8080/register?user=Bob"
 * curl "http://localhost:8080/users"
 */
