/*
 * Copyright 2025 Bloomberg Finance LP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <buildboxcommon_ocimanifest.h>
#include <stdexcept>

namespace buildboxcommon {

// Serialize OciManifestLayer to JSON
nlohmann::json OciManifestLayer::toJson() const
{
    nlohmann::json j;
    j["mediaType"] = d_mediaType;
    j["digest"] = d_digest;
    if (d_size.has_value())
        j["size"] = d_size.value();
    return j;
}

// Deserialize OciManifestLayer from JSON
OciManifestLayer OciManifestLayer::fromJson(const nlohmann::json &j)
{
    OciManifestLayer layer;
    if (!j.contains("mediaType") || !j.contains("digest")) {
        throw std::invalid_argument(
            "Missing required fields in OciManifestLayer");
    }
    layer.d_mediaType = j.at("mediaType").get<std::string>();
    layer.d_digest = j.at("digest").get<std::string>();
    if (j.contains("size"))
        layer.d_size = j.at("size").get<size_t>();
    return layer;
}

// Serialize OciManifest to JSON
nlohmann::json OciManifest::toJson() const
{
    nlohmann::json j;
    j["schemaVersion"] = d_schemaVersion;
    j["mediaType"] = d_mediaType;
    j["layers"] = nlohmann::json::array();
    for (const auto &layer : d_layers) {
        j["layers"].push_back(layer.toJson());
    }
    return j;
}

// Deserialize OciManifest from JSON
OciManifest OciManifest::fromJson(const nlohmann::json &j)
{
    OciManifest manifest;
    if (!j.contains("schemaVersion") || !j.contains("layers") ||
        !j.contains("mediaType")) {
        throw std::invalid_argument("Missing required fields in OciManifest");
    }
    manifest.d_schemaVersion = j.at("schemaVersion").get<int>();
    manifest.d_mediaType = j.at("mediaType").get<std::string>();
    for (const auto &layerJson : j.at("layers")) {
        manifest.d_layers.push_back(OciManifestLayer::fromJson(layerJson));
    }
    return manifest;
}

// Serialize OciPlatform to JSON
nlohmann::json OciPlatform::toJson() const
{
    nlohmann::json j;
    j["architecture"] = d_architecture;
    j["os"] = d_os;
    return j;
}

// Deserialize OciPlatform from JSON
OciPlatform OciPlatform::fromJson(const nlohmann::json &j)
{
    OciPlatform platform;
    if (!j.contains("architecture") || !j.contains("os")) {
        throw std::invalid_argument(
            "Missing required fields (architecture or os) in OciPlatform");
    }
    platform.d_architecture = j.at("architecture").get<std::string>();
    platform.d_os = j.at("os").get<std::string>();
    return platform;
}

// Serialize OciManifestIndexEntry to JSON
nlohmann::json OciManifestIndexEntry::toJson() const
{
    nlohmann::json j;
    j["mediaType"] = d_mediaType;
    j["digest"] = d_digest;
    if (d_size.has_value())
        j["size"] = d_size.value();
    j["platform"] = d_platform.toJson();
    return j;
}

// Deserialize OciManifestIndexEntry from JSON
OciManifestIndexEntry OciManifestIndexEntry::fromJson(const nlohmann::json &j)
{
    OciManifestIndexEntry entry;
    if (!j.contains("mediaType") || !j.contains("digest") ||
        !j.contains("platform")) {
        throw std::invalid_argument(
            "Missing required fields in OciManifestIndexEntry");
    }
    entry.d_mediaType = j.at("mediaType").get<std::string>();
    entry.d_digest = j.at("digest").get<std::string>();
    if (j.contains("size"))
        entry.d_size = j.at("size").get<size_t>();
    entry.d_platform = OciPlatform::fromJson(j.at("platform"));
    entry.d_reference = entry.d_digest; // Set reference to digest
    return entry;
}

// Serialize OciManifestIndex to JSON
nlohmann::json OciManifestIndex::toJson() const
{
    nlohmann::json j;
    j["schemaVersion"] = d_schemaVersion;
    j["mediaType"] = d_mediaType;
    j["manifests"] = nlohmann::json::array();
    for (const auto &manifest : d_manifests) {
        j["manifests"].push_back(manifest.toJson());
    }
    return j;
}

// Deserialize OciManifestIndex from JSON
OciManifestIndex OciManifestIndex::fromJson(const nlohmann::json &j)
{
    OciManifestIndex index;
    if (!j.contains("schemaVersion") || !j.contains("manifests") ||
        !j.contains("mediaType")) {
        throw std::invalid_argument(
            "Missing required fields in OciManifestIndex");
    }
    index.d_schemaVersion = j.at("schemaVersion").get<int>();
    index.d_mediaType = j.at("mediaType").get<std::string>();
    for (const auto &manifestJson : j.at("manifests")) {
        index.d_manifests.push_back(
            OciManifestIndexEntry::fromJson(manifestJson));
    }
    return index;
}

} // namespace buildboxcommon
