"""
STARWASTE - Network Protocol
Message serialization and protocol definitions

This module provides helpers for network message serialization using MessagePack.
Protocol uses raw MessagePack streaming (no length headers) and relies on
msgpack.Unpacker to parse message boundaries.
"""

import msgpack
from typing import Dict, Any, Iterator


# Safety limits for MessagePack streaming.
# These protect against accidental or malicious huge messages that could
# otherwise consume unbounded memory.
DEFAULT_MAX_BUFFER_SIZE = 8 * 1024 * 1024  # 8 MiB per connection buffer
DEFAULT_MAX_BIN_LEN = 8 * 1024 * 1024
DEFAULT_MAX_STR_LEN = 2 * 1024 * 1024
DEFAULT_MAX_ARRAY_LEN = 1_000_000
DEFAULT_MAX_MAP_LEN = 200_000


class Protocol:
    """
    Network protocol handler for MessagePack streaming.
    
    Messages are packed as raw MessagePack objects (no headers).
    Use msgpack.Unpacker(raw=False) on receiving end to parse stream.
    Unpacker handles message boundaries automatically.
    """
    
    @staticmethod
    def pack(data: Dict[str, Any]) -> bytes:
        """
        Pack a message for streaming transmission.
        
        Returns raw MessagePack bytes. The receiving end should use
        msgpack.Unpacker to parse the stream.
        
        Args:
            data: Dictionary to serialize
            
        Returns:
            Raw MessagePack bytes
        """
        return msgpack.packb(data, use_bin_type=True)
    
    @staticmethod
    def create_unpacker(raw: bool = False) -> msgpack.Unpacker:
        """
        Create a streaming Unpacker for receiving messages.
        
        Usage:
            unpacker = Protocol.create_unpacker()
            # ... read data from socket/stream ...
            unpacker.feed(chunk)
            for message in unpacker:
                # process message dict
                pass
        
        Args:
            raw: If True, keys will be bytes; if False (default), decoded as str
            
        Returns:
            msgpack.Unpacker instance ready to parse stream
        """
        return msgpack.Unpacker(
            raw=raw,
            strict_map_key=True,
            max_buffer_size=DEFAULT_MAX_BUFFER_SIZE,
            max_bin_len=DEFAULT_MAX_BIN_LEN,
            max_str_len=DEFAULT_MAX_STR_LEN,
            max_array_len=DEFAULT_MAX_ARRAY_LEN,
            max_map_len=DEFAULT_MAX_MAP_LEN,
        )
    
    @staticmethod
    def unpack_stream(data: bytes, raw: bool = False) -> Iterator[Dict[str, Any]]:
        """
        Unpack one or more messages from a byte buffer (helper).
        
        Args:
            data: Bytes containing one or more MessagePack objects
            raw: If True, keys will be bytes; if False (default), decoded as str
            
        Yields:
            Unpacked message dictionaries
        """
        unpacker = Protocol.create_unpacker(raw=raw)
        unpacker.feed(data)
        yield from unpacker
