//-------------------------------------------------------------------------------------------------
// <copyright file="ImdsOperations.cpp" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------

#include <curl/curl.h>
#include <json/json.h>
#include <chrono>
#include <thread>
#include <math.h>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <stdio.h>
#include "Logging.h"
#include "ImdsOperations.h"
#include "Exceptions.h"
#include "AttestationHelper.h"
#include "AttestationClientImpl.h"
#include "AttestationLibUtils.h"
#include "AttestationLibConst.h"
#include "TpmUnseal.h"
#include "HttpClient.h"
#include "AttestationLibTelemetry.h"

//azure local
#ifdef AZURE_LOCAL
#include <hw_evidence_manager.h>
#endif

#ifndef AZURE_LOCAL

// IMDS endpoint for getting the VCek certificate
constexpr char imds_endpoint[] = "http://169.254.169.254/metadata";
constexpr char vcek_cert_path[] = "/THIM/amd/certification";

attest::AttestationResult ImdsOperations::GetVCekCert(std::string& vcek_cert) {
    AttestationResult result(AttestationResult::ErrorCode::SUCCESS);

    std::string http_response;
    std::string url = std::string(imds_endpoint) +
                      std::string(vcek_cert_path);

    HttpClient http_client;
    if ((result = http_client.InvokeHttpImdsRequest(http_response, url, HttpClient::HttpVerb::GET)).code_ != AttestationResult::ErrorCode::SUCCESS) {
        CLIENT_LOG_ERROR("Failed to retrieve VCek certificate from IMDS: %s",
            result.description_.c_str());
        if (attest::telemetry_reporting.get() != nullptr) {
            attest::telemetry_reporting->UpdateEvent("Get VCekCert", "Failed to retrive VCek certificate from IMDS", attest::TelemetryReportingBase::EventLevel::IMDS_QUERY_VCek_CERT);
        }
        return result;
    }

    Json::Value root;
    Json::Reader reader;
    bool parsing_successful = reader.parse(http_response, root);
    if (!parsing_successful) {
        CLIENT_LOG_ERROR("Invalid JSON reponse from IMDS");
        result.code_ = AttestationResult::ErrorCode::ERROR_INVALID_JSON_RESPONSE;
        result.description_ = std::string("Invalid JSON reponse from IMDS");
        return result;
    }

    std::string cert = root["vcekCert"].asString();
    std::string chain = root["certificateChain"].asString();
    if (cert.empty() ||
        chain.empty()) {
        CLIENT_LOG_ERROR("Empty VCek cert received from THIM");
        result.code_ = AttestationResult::ErrorCode::ERROR_EMPTY_VCEK_CERT;
        result.description_ = std::string("Empty VCek cert received from THIM");
        return result;
    }

    CLIENT_LOG_DEBUG("VCek cert received from IMDS successfully");
    std::string cert_chain = cert + chain;
    vcek_cert = attest::base64::base64_encode(cert_chain);

    return result;
}

#else // AZURE_LOCAL

// Note: This Azure Local GetVCekCert implementation is no longer called.
// Evidence gathering (including endorsements) is now handled directly
// in AttestationClientImpl::GetIsolationInfo via the evidence SDK.
attest::AttestationResult ImdsOperations::GetVCekCert(std::string& vcek_cert) {
    AttestationResult result(AttestationResult::ErrorCode::SUCCESS);

    uint32_t endorsements_size = 0;
    endorsement_options options{ false };
    hw_evidence_result get_endorsements_result = get_endorsements(options, nullptr, &endorsements_size);

    if (hw_evidence_result_failed(get_endorsements_result))
    {
        if (get_endorsements_result == HW_EVIDENCE_ERROR_NOT_SUPPORTED && get_hardware_capabilities() == CONFIDENTIAL_COMPUTE_TDX)
        {
            CLIENT_LOG_ERROR("TDX hardware does not support VCEK");
            result.code_ = AttestationResult::ErrorCode::ERROR_EMPTY_VCEK_CERT;
            result.description_ = std::string("Incompatable hardware does not support VCEK");
            return result;

        }
        else
        {
            CLIENT_LOG_ERROR("Failed to get endorsements size");
            result.code_ = AttestationResult::ErrorCode::ERROR_EMPTY_RESPONSE; //Bugbug discuss better errors
            result.description_ = std::string("get_endorsements_result failed: " + std::to_string(static_cast<int>(get_endorsements_result)));
            return  result;
        }
    }

    std::vector<uint8_t> endorsements(endorsements_size);
    get_endorsements_result = get_endorsements(options, endorsements.data(), &endorsements_size);

    if (hw_evidence_result_failed(get_endorsements_result))
    {
        CLIENT_LOG_ERROR("Failed to get endorsements size");
        result.code_ = AttestationResult::ErrorCode::ERROR_EMPTY_RESPONSE; //Bugbug discuss better errors
        result.description_ = std::string("get_endorsements_result failed: " + std::to_string(static_cast<int>(get_endorsements_result)));
        return  result;
    }


    vcek_cert = attest::base64::binary_to_base64(endorsements);

    return result;
}

#endif // AZURE_LOCAL
