/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include <openssl/evp.h>
#include <span>
#include <vector>

namespace sdi_toolBox::crypto
{
//--------------------------------------------------------------
class Base64
{
  public:
  Base64()                                 = default;     // Default constructor
  virtual ~Base64()                        = default;     // Default destructor
  Base64(const Base64 &obj)                = delete;      // Copy constructor
  Base64(Base64 &&obj) noexcept            = delete;      // Move constructor
  Base64 &operator=(const Base64 &obj)     = delete;      // Copy assignment operator
  Base64 &operator=(Base64 &&obj) noexcept = delete;      // Move assignment operator

  static std::string          encode(const std::span<uint8_t> &binary_data);     // Encodes binary data into a Base64 string
  static std::vector<uint8_t> decode(const std::string &base64_string);          // Decodes a Base64 string into binary data
};
//--------------------------------------------------------------
/* Encodes binary data into a Base64 string */
inline std::string Base64::encode(const std::span<uint8_t> &binary_data)
{
  if (binary_data.empty())
    return "";

  // Calculate the length of the encoded data
  const size_t output_len_max = EVP_ENCODE_LENGTH(binary_data.size());

  // Allocate output buffer (with null terminator)
  std::vector<char> output_buffer(output_len_max + 1, 0);

  // OpenSSL encoding
  const auto final_len = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(output_buffer.data()),
                                         binary_data.data(),
                                         binary_data.size());
  if (final_len < 0)
    throw std::runtime_error("Error: Base64 encoding failed");

  return std::string(output_buffer.data());
}
//--------------------------------------------------------------
/* Decodes a Base64 string into binary data */
inline std::vector<uint8_t> Base64::decode(const std::string &base64_string)
{
  if (base64_string.empty())
    return {};

  // Calculate the length of the decoded data
  const auto output_len_max = EVP_DECODE_LENGTH(base64_string.size());

  // Allocate output buffer
  std::vector<uint8_t> output_buffer(output_len_max);

  // OpenSSL decoding
  const auto final_len = EVP_DecodeBlock(output_buffer.data(),
                                         reinterpret_cast<const unsigned char *>(base64_string.data()),
                                         static_cast<int>(base64_string.size()));
  if (final_len < 0)
    throw std::runtime_error("Error: Base64 decoding failed: Invalid data");
  output_buffer.resize(final_len);

  return output_buffer;
}
//--------------------------------------------------------------
}     // namespace sdi_toolBox::crypto
