// Copyright 2021,2024 Jeisson Hidalgo-Cespedes. ECCI-UCR. CC BY 4.0

#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

#include "Factorization.hpp"
#include "FactWebApp.hpp"
#include "HttpRequest.hpp"
#include "HttpResponse.hpp"
#include "Util.hpp"

static const char* const PREFIX1 = "/fact";
static const char* const PREFIX2 = "/fact?number=";

// TODO(you): Do not duplicate code for other apps, such as GoldbachWebApp.
// Move common code to a base class for Factorization and Goldbach apps

bool FactWebApp::handleHttpRequest(HttpRequest& httpRequest,
    HttpResponse& httpResponse) {
  // If the request starts with "fact/" is for this web app
  if (httpRequest.getURI().rfind(PREFIX1, 0) == 0) {
    return this->serveFactorization(httpRequest, httpResponse);
  }
  // Unrecognized request
  return false;
}

void FactWebApp::addToHomepage(HttpRequest&, HttpResponse& httpResponse) {
  httpResponse.body()
    << "  <h2>" << "Prime factorization" << "</h2>\n"
    << "  <form method=get action=" << PREFIX1 << ">\n"
    << "    <label for=number>Number</label>\n"
    << "    <input type=text id=number name=number "
              "pattern=\"\\[+\\-]?d+(,[+\\-]?\\d+)*\" required>\n"
    << "    <button type=submit>Factorize</button>\n"
    << "  </form>\n"
    << "</html>\n";
}

bool FactWebApp::serveFactorization(HttpRequest& httpRequest,
    HttpResponse& httpResponse) {
  // Replace %xx hexadecimal codes by their ASCII symbols
  // Sanitize <html> code that client may want to inject
  const std::string& uri = HttpApp::sanitizeHtml(Util::decodeURI(
      httpRequest.getURI()));
  // Build the body of the response
  this->serveHeader(httpResponse, "Prime factorization");
  httpResponse.body()
    << "  <p>request: " << uri << "</p>\n"
    << "  <ol>\n";
  this->factorizeNumbers(uri, httpResponse);
  httpResponse.body()
    << "  </ol>\n"
    << "  <hr>\n"
    << "  <p><a href=\"/\">Back</a></p>\n"
    << "</html>\n";
  // Send the response to the client (user agent)
  return httpResponse.send();
}

void FactWebApp::factorizeNumbers(const std::string& uri,
    HttpResponse& httpResponse) {
  // Numbers were asked in the form "/fact/123,45,-7899" or "/fact?number=13"
  // Determine the position where numbers start. Assume PREFIX1 is used
  size_t numbersStart = std::strlen(PREFIX1);
  // If URI starts with PREFIX2 instead of PREFIX1
  if (uri.rfind(PREFIX2, 0) == 0) {
    // Numbers starts after the PREFIX2 length
    numbersStart = std::strlen(PREFIX2);
  }
  // TODO(you): Use arbitrary precision for numbers larger than int64_t
  const std::vector<std::string>& texts =
      Util::split(uri.substr(numbersStart), ",", true);
  // For each asked number, provide its prime factorization
  for (size_t index = 0; index < texts.size(); ++index) {
    try {
      // Convert the text to a number. Provide an error message if not
      size_t end = 0;
      const int64_t number = std::stoll(texts[index], &end);
      if (end != texts[index].length()) {
        throw std::runtime_error("invalid number " + texts[index]);
      }
      // Text was converted to number, provide its prime factorization
      httpResponse.body()
        << "    <li>" << Factorization(number) << std::endl;
    } catch (const std::exception& exception) {
      // Text was not a valid number, report an error to user
      httpResponse.body() << "    <li class=err>" << texts[index]
          << ": invalid number\n";
    }
  }
}
