/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include <filesystem>
#include <openssl/evp.h>
#include <span>
#include <string>

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

  static std::string calculateDataHash(const std::span<const uint8_t> &data, const EVP_MD *md_type);     // Calculate digital hash from data buffer
};
//--------------------------------------------------------------
/* Calculate digital hash from data buffer */
inline std::string DigitalHash::calculateDataHash(const std::span<const uint8_t> &data, const EVP_MD *md_type)
{
  // Initialize OpenSSL digest context
  EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
  if (!mdctx)
    throw std::runtime_error("Error: Failed to create EVP_MD_CTX");

  // Initialize digest operation
  if (EVP_DigestInit_ex(mdctx, md_type, nullptr) != 1)
  {
    EVP_MD_CTX_free(mdctx);
    throw std::runtime_error("Error: EVP_DigestInit_ex failed");
  }

  // Update digest with data
  if (EVP_DigestUpdate(mdctx, data.data(), data.size()) != 1)
  {
    EVP_MD_CTX_free(mdctx);
    throw std::runtime_error("Error: EVP_DigestUpdate failed");
  }

  // Finalize digest computation
  std::vector<uint8_t> hash(EVP_MD_size(md_type));
  unsigned int         hash_len = 0;
  if (EVP_DigestFinal_ex(mdctx, hash.data(), &hash_len) != 1)
  {
    EVP_MD_CTX_free(mdctx);
    throw std::runtime_error("Error: EVP_DigestFinal_ex failed");
  }

  // Clean up
  EVP_MD_CTX_free(mdctx);

  // Convert hash to hexadecimal string
  std::string hash_hex;
  for (unsigned int i = 0; i < hash_len; ++i)
    hash_hex += std::format("{:02x}", hash[i]);

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