#ifndef DEVICE_CTRL_PROTOBUF_UTIL_H
#define DEVICE_CTRL_PROTOBUF_UTIL_H
#include "common/utils/file_util.h"
#include <fstream>
#include <google/protobuf/util/json_util.h>

namespace zsy::PbUtil
{
    template<typename T>
    concept ProtobufMessage = std::derived_from<T, google::protobuf::Message>;

    inline std::string toJsonStr(const google::protobuf::Message &msg)
    {
        google::protobuf::util::JsonPrintOptions options;
        options.always_print_fields_with_no_presence = true;
        options.preserve_proto_field_names = true;
        options.add_whitespace = true;
        std::string json_string;

        const auto status = google::protobuf::util::MessageToJsonString(msg, &json_string, options);
        return status.ok() ? json_string : "";
    }

    template<ProtobufMessage T>
    std::optional<T> fromJsonStr(const std::string &json_str)
    {
        if (json_str.empty())return std::nullopt;
        google::protobuf::json::ParseOptions options;
        options.ignore_unknown_fields = true;
        T msg;
        const auto status = google::protobuf::util::JsonStringToMessage(json_str, &msg, options);
        return status.ok() ? std::make_optional(msg) : std::nullopt;
    }

    inline bool writeBin(const std::string &file_path, const google::protobuf::Message &msg)
    {
        std::fstream output(file_path, std::ios::out | std::ios::binary | std::ios::trunc);
        return output ? msg.SerializeToOstream(&output) : false;
    }

    template<ProtobufMessage T>
    std::optional<T> readBin(const std::string &file_path)
    {
        if (!FileUtil::exists(file_path))
        {
            LOGGER_ERROR("文件不存在: {}", file_path);
            return std::nullopt;
        }

        std::fstream input(file_path, std::ios::in | std::ios::binary);
        if (T msg; input && msg.ParseFromIstream(&input))
        {
            return std::make_optional(msg);
        }
        return std::nullopt;
    }

    inline bool writeJson(const std::string &file_path, const google::protobuf::Message &msg)
    {
        const auto json_str = toJsonStr(msg);
        if (std::fstream output(file_path, std::ios::out | std::ios::trunc); output)
        {
            output << json_str;
            return true;
        }
        return false;
    }

    template<ProtobufMessage T>
    std::optional<T> readJson(const std::string &json_file_path)
    {
        if (!FileUtil::exists(json_file_path))
        {
            LOGGER_ERROR("文件不存在: {}", json_file_path);
            return std::nullopt;
        }

        if (std::fstream input(json_file_path, std::ios::in); input)
        {
            T msg;
            const std::string json_str((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
            return fromJsonStr<T>(json_str);
        }
        return std::nullopt;
    }
}

#endif // DEVICE_CTRL_PROTOBUF_UTIL_H