#include <softbus_transport/UplinkWire.h>

#include <chrono>
#include <nlohmann/json.hpp>

namespace softbus::transport {

namespace {

static const char kBase64Table[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

} // namespace

std::string base64Encode(const uint8_t* data, std::size_t size)
{
    std::string out;
    out.reserve(((size + 2) / 3) * 4);
    for (std::size_t i = 0; i < size; i += 3) {
        const uint32_t block = (static_cast<uint32_t>(data[i]) << 16)
            | ((i + 1 < size ? data[i + 1] : 0) << 8)
            | (i + 2 < size ? data[i + 2] : 0);
        out.push_back(kBase64Table[(block >> 18) & 0x3F]);
        out.push_back(kBase64Table[(block >> 12) & 0x3F]);
        out.push_back(i + 1 < size ? kBase64Table[(block >> 6) & 0x3F] : '=');
        out.push_back(i + 2 < size ? kBase64Table[block & 0x3F] : '=');
    }
    return out;
}

std::vector<uint8_t> base64Decode(const std::string& encoded)
{
    auto val = [](char c) -> int {
        if (c >= 'A' && c <= 'Z') return c - 'A';
        if (c >= 'a' && c <= 'z') return c - 'a' + 26;
        if (c >= '0' && c <= '9') return c - '0' + 52;
        if (c == '+') return 62;
        if (c == '/') return 63;
        return -1;
    };

    std::vector<uint8_t> out;
    int buffer = 0;
    int bits = 0;
    for (char c : encoded) {
        if (c == '=' || c == '\n' || c == '\r') {
            continue;
        }
        const int v = val(c);
        if (v < 0) {
            continue;
        }
        buffer = (buffer << 6) | v;
        bits += 6;
        if (bits >= 8) {
            bits -= 8;
            out.push_back(static_cast<uint8_t>((buffer >> bits) & 0xFF));
        }
    }
    return out;
}

std::string encodeWireMessage(const softbus::core::RawPacket& packet)
{
    nlohmann::json j{
        {"type", "rawPacket"},
        {"traceId", packet.traceId},
        {"timestampNs", packet.timestampNs},
        {"runtimeDeviceId", packet.runtimeDeviceId},
        {"sourceId", packet.sourceId},
        {"protocolTag", packet.protocolTag},
        {"payloadBase64", base64Encode(packet.payload.data(), packet.payload.size())}
    };
    return j.dump() + "\n";
}

std::string encodeWireMessage(const softbus::api::ExecuteCommand& command)
{
    return command.toJson().dump() + "\n";
}

std::string encodeWireMessage(const softbus::core::SoftbusAck& ack)
{
    return ack.toJson().dump() + "\n";
}

std::string encodeWireMessage(const std::string& deviceId)
{
    nlohmann::json j{
        {"type", "heartbeat"},
        {"deviceId", deviceId},
        {"timestampNs", static_cast<std::uint64_t>(
            std::chrono::duration_cast<std::chrono::nanoseconds>(
                std::chrono::system_clock::now().time_since_epoch()).count())}
    };
    return j.dump() + "\n";
}

std::optional<WireMessage> decodeWireMessage(const std::string& line)
{
    if (line.empty()) {
        return std::nullopt;
    }
    const auto j = nlohmann::json::parse(line, nullptr, false);
    if (j.is_discarded()) {
        return std::nullopt;
    }
    const std::string type = j.value("type", "");
    WireMessage msg;
    if (type == "rawPacket") {
        msg.type = WireMessageType::RawPacket;
        msg.rawPacket.traceId = j.value("traceId", "");
        msg.rawPacket.timestampNs = j.value("timestampNs", int64_t{0});
        msg.rawPacket.runtimeDeviceId = j.value("runtimeDeviceId", "");
        msg.rawPacket.sourceId = j.value("sourceId", "");
        msg.rawPacket.protocolTag = j.value("protocolTag", "");
        msg.rawPacket.payload = base64Decode(j.value("payloadBase64", ""));
        return msg;
    }
    if (type == "executeCommand") {
        msg.type = WireMessageType::ExecuteCommand;
        msg.command = softbus::api::ExecuteCommand::fromJson(j);
        return msg;
    }
    if (type == "ack") {
        msg.type = WireMessageType::Ack;
        msg.ack = softbus::core::SoftbusAck::fromJson(j);
        return msg;
    }
    if (type == "heartbeat") {
        msg.type = WireMessageType::Heartbeat;
        msg.heartbeatDeviceId = j.value("deviceId", "");
        return msg;
    }
    return std::nullopt;
}

} // namespace softbus::transport
