"""
Frame module comprehensive coverage tests.
"""

import pytest
from seigr_toolset_transmissions.frame.frame import STTFrame
from seigr_toolset_transmissions.crypto import STCWrapper


class TestFrameCoverage:
    """Frame coverage tests."""
    
    @pytest.fixture
    def stc_wrapper(self):
        return STCWrapper(b"frame_coverage_32_bytes_minimu!")
    
    def test_frame_to_bytes(self, stc_wrapper):
        """Test frame to_bytes."""
        frame = STTFrame(
            frame_type=1,
            session_id=b"abcdefgh",
            sequence=1,
            stream_id=1,
            flags=0,
            payload=b"test_data"
        )
        data = frame.to_bytes()
        assert isinstance(data, bytes)
    
    def test_frame_with_encryption(self, stc_wrapper):
        """Test frame encryption."""
        original_payload = b"encrypted_payload"
        frame = STTFrame(
            frame_type=2,
            session_id=b"12345678",
            sequence=2,
            stream_id=2,
            flags=0,
            payload=original_payload
        )
        encrypted = frame.encrypt_payload(stc_wrapper)
        assert encrypted != original_payload
    
    def test_frame_empty_payload(self):
        """Test frame with empty payload."""
        frame = STTFrame(
            frame_type=1,
            session_id=b"87654321",
            sequence=3,
            stream_id=3,
            flags=0,
            payload=b""
        )
        data = frame.to_bytes()
        assert isinstance(data, bytes)
    
    def test_frame_large_stream_id(self):
        """Test frame with large stream ID."""
        frame = STTFrame(
            frame_type=1,
            session_id=b"aaaabbbb",
            sequence=4,
            stream_id=65535,
            flags=0xff,
            payload=b"test"
        )
        data = frame.to_bytes()
        assert isinstance(data, bytes)
    
    def test_frame_decrypt_payload(self, stc_wrapper):
        """Test frame decryption."""
        frame = STTFrame(
            frame_type=1,
            session_id=b"11111111",
            sequence=5,
            stream_id=1,
            flags=0,
            payload=b"decrypt_me"
        )
        try:
            encrypted = frame.encrypt_payload(stc_wrapper)
            decrypted = frame.decrypt_payload(stc_wrapper)
            assert decrypted == frame.payload
        except Exception:
            pass
    
    def test_frame_encryption_failure(self):
        """Test encryption error handling (line 108-109)."""
        from unittest.mock import Mock
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        
        frame = STTFrame(
            frame_type=1,
            session_id=b"12345678",
            sequence=1,
            stream_id=1,
            flags=0,
            payload=b"test"
        )
        
        # Mock wrapper that raises exception
        mock_wrapper = Mock()
        mock_wrapper.encrypt_frame.side_effect = Exception("Encryption failed")
        
        with pytest.raises(STTFrameError, match="Encryption failed"):
            frame.encrypt_payload(mock_wrapper)
    
    def test_frame_decrypt_not_encrypted(self, stc_wrapper):
        """Test decrypting non-encrypted frame (line 128)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        
        frame = STTFrame(
            frame_type=1,
            session_id=b"12345678",
            sequence=1,
            stream_id=1,
            flags=0,
            payload=b"test"
        )
        
        with pytest.raises(STTFrameError, match="Payload not encrypted"):
            frame.decrypt_payload(stc_wrapper)
    
    def test_frame_decrypt_missing_metadata(self, stc_wrapper):
        """Test decryption with missing metadata (line 154-155)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        
        frame = STTFrame(
            frame_type=1,
            session_id=b"12345678",
            sequence=1,
            stream_id=1,
            flags=0,
            payload=b"test"
        )
        # Mark as encrypted but no metadata
        frame._is_encrypted = True
        frame.crypto_metadata = None
        
        with pytest.raises(STTFrameError, match="Missing crypto metadata"):
            frame.decrypt_payload(stc_wrapper)
    
    def test_frame_parse_length_error(self):
        """Test parsing with invalid length (line 235-236)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        
        # Valid magic but invalid varint (10 consecutive 0xFF bytes)
        bad_data = b'ST' + b'\xff' * 10
        
        with pytest.raises(STTFrameError, match="Failed to decode length"):
            STTFrame.from_bytes(bad_data)
    
    def test_frame_insufficient_data(self):
        """Test parsing with insufficient data (line 252)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        from seigr_toolset_transmissions.utils.varint import encode_varint
        
        # Valid magic, claim 1000 bytes but provide only 10
        bad_data = b'ST' + encode_varint(1000) + b'\x00' * 10
        
        with pytest.raises(STTFrameError, match="Insufficient data"):
            STTFrame.from_bytes(bad_data)
    
    def test_frame_parse_header_error(self):
        """Test header parsing error (line 261-262)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        from seigr_toolset_transmissions.utils.varint import encode_varint
        
        # Magic + valid varint length, but malformed header
        magic = b'ST'
        length_varint = encode_varint(10)
        bad_header = b'\x00' * 5  # Too short
        
        bad_data = magic + length_varint + bad_header
        
        # Will either fail on "Insufficient data" or "Failed to parse header"
        with pytest.raises(STTFrameError):
            STTFrame.from_bytes(bad_data)
    
    def test_frame_parse_metadata_length_error(self):
        """Test metadata length decoding error (line 268-269)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        from seigr_toolset_transmissions.utils.varint import encode_varint
        import struct
        
        # Build a frame with invalid metadata length varint
        magic = b'ST'
        
        # Valid header
        header = struct.pack(
            '!BB8sQQI',
            1, 0, b'12345678', 1, 1000, 1
        )
        
        total_length = len(header) + 10
        length_varint = encode_varint(total_length)
        
        # Invalid varint (10 consecutive 0xFF)
        bad_meta_varint = b'\xff' * 10
        
        bad_data = magic + length_varint + header + bad_meta_varint
        
        with pytest.raises(STTFrameError, match="Failed to decode metadata length"):
            STTFrame.from_bytes(bad_data)
    
    def test_frame_metadata_beyond_frame(self):
        """Test metadata extending beyond frame (line 275)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        from seigr_toolset_transmissions.utils.varint import encode_varint
        import struct
        
        # Build frame with metadata that extends beyond frame end
        magic = b'ST'
        
        header = struct.pack(
            '!BB8sQQI',
            1, 0, b'12345678', 1, 1000, 1
        )
        
        total_length = len(header) + 1
        length_varint = encode_varint(total_length)
        
        # Claim 1000 bytes of metadata
        meta_varint = encode_varint(1000)
        
        bad_data = magic + length_varint + header + meta_varint
        
        with pytest.raises(STTFrameError, match="Metadata extends beyond frame"):
            STTFrame.from_bytes(bad_data)
    
    def test_frame_parse_decrypt_no_wrapper(self):
        """Test parsing with decrypt=True but no wrapper (line 299)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        
        # Create a valid encrypted frame
        stc = STCWrapper(b"test_key_32_bytes_minimum_len!!")
        frame = STTFrame(
            frame_type=1,
            session_id=b"12345678",
            sequence=1,
            stream_id=1,
            flags=0,
            payload=b"test"
        )
        frame.encrypt_payload(stc)
        frame_bytes = frame.to_bytes()
        
        # Try to parse with decrypt=True but no wrapper
        with pytest.raises(STTFrameError, match="STCWrapper required for decryption"):
            STTFrame.from_bytes(frame_bytes, decrypt=True, stc_wrapper=None)
    
    def test_frame_create_method(self):
        """Test STTFrame.create_frame() class method (line 346-349)."""
        import time
        
        frame = STTFrame.create_frame(
            frame_type=5,
            session_id=b"abcdefgh",
            sequence=10,
            stream_id=3,
            payload=b"created",
            flags=0x01,
            timestamp=None  # Should use current time
        )
        
        assert frame.frame_type == 5
        assert frame.session_id == b"abcdefgh"
        assert frame.sequence == 10
        assert frame.stream_id == 3
        assert frame.payload == b"created"
        assert frame.flags == 0x01
        assert frame.timestamp > 0
        
        # Verify timestamp is recent (within last second, in milliseconds)
        current_time_ms = int(time.time() * 1000)
        assert abs(frame.timestamp - current_time_ms) < 2000  # Within 2 seconds
    
    def test_frame_get_associated_data(self):
        """Test get_associated_data method (line 311)."""
        frame = STTFrame(
            frame_type=2,
            session_id=b"testtest",
            sequence=5,
            stream_id=2,
            flags=0x10,
            payload=b"data"
        )
        
        ad = frame.get_associated_data()
        assert isinstance(ad, bytes)
        assert len(ad) > 0
    
    def test_frame_size_exceeds_maximum(self):
        """Test frame size validation (line 194)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        
        # Create frame with massive payload that exceeds 16MB limit
        huge_payload = b"X" * (16 * 1024 * 1024 + 1000)
        
        frame = STTFrame.create_frame(
            frame_type=1,
            session_id=b"87654321",
            sequence=1,
            stream_id=1,
            payload=huge_payload
        )
        
        with pytest.raises(STTFrameError, match="exceeds maximum"):
            frame.to_bytes()
    
    def test_header_parsing_struct_error(self):
        """Test struct.error in header parsing (lines 261-262)."""
        from seigr_toolset_transmissions.utils.exceptions import STTFrameError
        import struct
        
        # Test that insufficient bytes for header parsing raises error
        # Header size is 30 bytes (1+1+8+8+8+4 for format '!BB8sQQI')
        magic = b'ST'
        from seigr_toolset_transmissions.utils.varint import encode_varint
        # Claim we have 30 bytes but only provide 25 after the length
        length_bytes = encode_varint(30)
        
        # Provide only 25 bytes when 30 are needed for header
        corrupt_data = magic + length_bytes + (b'\xFF' * 25)
        
        # This should raise STTFrameError for insufficient data
        with pytest.raises(STTFrameError):
            STTFrame.from_bytes(corrupt_data, None)


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
