#ifdef ESP_PLATFORM
#include <sdkconfig.h>
#endif

#if defined(CONFIG_SOC_WIFI_SUPPORTED) || defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED)

#include <Tactility/app/chat/ChatProtocol.h>
#include <Tactility/service/espnow/EspNow.h>

#include <algorithm>
#include <cstring>

namespace tt::app::chat {

bool serializeTextMessage(uint32_t senderId, uint32_t targetId,
                          const std::string& senderName, const std::string& target,
                          const std::string& message, std::vector<uint8_t>& out) {
    // Validate input lengths (min and max)
    if (senderName.size() < MIN_NICKNAME_LEN || senderName.size() > MAX_NICKNAME_LEN) {
        return false;
    }
    if (target.size() > MAX_TARGET_LEN) {
        return false;  // MIN_TARGET_LEN is 0, so empty (broadcast) is allowed
    }
    if (message.size() < MIN_MESSAGE_LEN) {
        return false;
    }

    // Calculate payload size: nickname + null + target + null + message
    size_t payloadSize = senderName.size() + 1 + target.size() + 1 + message.size();

    // Check against ESP-NOW limits (guard against underflow if getMaxDataLength < HEADER_SIZE)
    size_t maxData = service::espnow::getMaxDataLength();
    if (maxData <= HEADER_SIZE) {
        return false;
    }
    size_t maxPayload = maxData - HEADER_SIZE;
    if (payloadSize > maxPayload || payloadSize > 255) {
        return false;  // payload_size is uint8_t
    }

    // Build header
    MessageHeader header = {
        .magic = CHAT_MAGIC_V2,
        .protocol_version = PROTOCOL_VERSION,
        .from = senderId,
        .to = targetId,
        .payload_type = static_cast<uint8_t>(PayloadType::TextMessage),
        .payload_size = static_cast<uint8_t>(payloadSize)
    };

    // Allocate output buffer
    out.resize(HEADER_SIZE + payloadSize);

    // Copy header to output
    memcpy(out.data(), &header, HEADER_SIZE);

    // Build payload: nickname\0 + target\0 + message
    uint8_t* payload = out.data() + HEADER_SIZE;
    size_t offset = 0;

    memcpy(payload + offset, senderName.c_str(), senderName.size() + 1);
    offset += senderName.size() + 1;

    memcpy(payload + offset, target.c_str(), target.size() + 1);
    offset += target.size() + 1;

    memcpy(payload + offset, message.c_str(), message.size());
    // Note: message is NOT null-terminated in wire format (length is implicit)

    return true;
}

bool deserializeMessage(const uint8_t* data, size_t length, ParsedMessage& out) {
    // Minimum: header + min_nickname + null + min_target + null + min_message
    // = 16 + 2 + 1 + 0 + 1 + 1 = 21 bytes
    constexpr size_t MIN_PACKET_SIZE = HEADER_SIZE + MIN_NICKNAME_LEN + 1 + MIN_TARGET_LEN + 1 + MIN_MESSAGE_LEN;
    if (length < MIN_PACKET_SIZE) {
        return false;
    }

    // Copy header to aligned struct
    MessageHeader header;
    memcpy(&header, data, HEADER_SIZE);

    // Validate header
    if (header.magic != CHAT_MAGIC_V2) {
        return false;
    }

    if (header.protocol_version != PROTOCOL_VERSION) {
        return false;
    }

    // Validate payload size
    if (header.payload_size != length - HEADER_SIZE) {
        return false;
    }

    // Only handle text messages for now
    if (header.payload_type != static_cast<uint8_t>(PayloadType::TextMessage)) {
        return false;
    }

    // Parse payload
    const uint8_t* payload = data + HEADER_SIZE;
    size_t payloadLen = header.payload_size;

    // Find nickname (null-terminated)
    const char* nicknameStart = reinterpret_cast<const char*>(payload);
    size_t nicknameLen = strnlen(nicknameStart, payloadLen);
    if (nicknameLen >= payloadLen) {
        return false;  // No null terminator found
    }

    size_t offset = nicknameLen + 1;
    size_t remaining = payloadLen - offset;

    // Find target (null-terminated)
    const char* targetStart = reinterpret_cast<const char*>(payload + offset);
    size_t targetLen = strnlen(targetStart, remaining);
    if (targetLen >= remaining) {
        return false;  // No null terminator found
    }

    offset += targetLen + 1;
    remaining = payloadLen - offset;

    // Rest is the message (not null-terminated)
    const char* messageStart = reinterpret_cast<const char*>(payload + offset);

    // Validate field lengths (min and max)
    if (nicknameLen < MIN_NICKNAME_LEN || nicknameLen > MAX_NICKNAME_LEN) {
        return false;
    }
    if (targetLen > MAX_TARGET_LEN) {
        return false;
    }
    if (remaining < MIN_MESSAGE_LEN) {
        return false;
    }

    // Populate output
    out.senderId = header.from;
    out.targetId = header.to;
    out.senderName = std::string(nicknameStart, nicknameLen);
    out.target = std::string(targetStart, targetLen);
    out.message = std::string(messageStart, remaining);

    return true;
}

size_t getMaxMessageLength(size_t nicknameLen, size_t targetLen) {
    // Guard against underflow if getMaxDataLength < HEADER_SIZE
    size_t maxData = service::espnow::getMaxDataLength();
    if (maxData <= HEADER_SIZE) {
        return 0;
    }
    size_t maxPayload = maxData - HEADER_SIZE;

    // Payload: nickname + null + target + null + message
    size_t overhead = nicknameLen + 1 + targetLen + 1;
    if (overhead >= maxPayload || overhead > 255) {
        return 0;
    }
    // Cap at 255 since payload_size is uint8_t
    size_t maxFromEspNow = maxPayload - overhead;
    size_t maxFromPayloadSize = 255 - overhead;
    return std::min(maxFromEspNow, maxFromPayloadSize);
}

} // namespace tt::app::chat

#endif // CONFIG_SOC_WIFI_SUPPORTED || CONFIG_SLAVE_SOC_WIFI_SUPPORTED
