import os
import struct
import time
import wave
import pytest
import coremusic as cm
import coremusic.capi as capi


class TestAudioFileStreamConstants:
    """Test AudioFileStream constants access"""

    def test_audio_file_stream_property_constants(self):
        """Test AudioFileStream property constants"""
        assert (
            capi.get_audio_file_stream_property_ready_to_produce_packets() is not None
        )
        assert capi.get_audio_file_stream_property_file_format() is not None
        assert capi.get_audio_file_stream_property_data_format() is not None
        assert capi.get_audio_file_stream_property_format_list() is not None
        assert capi.get_audio_file_stream_property_magic_cookie_data() is not None
        assert capi.get_audio_file_stream_property_audio_data_byte_count() is not None
        assert capi.get_audio_file_stream_property_audio_data_packet_count() is not None
        assert capi.get_audio_file_stream_property_maximum_packet_size() is not None
        assert capi.get_audio_file_stream_property_data_offset() is not None
        assert capi.get_audio_file_stream_property_channel_layout() is not None
        assert capi.get_audio_file_stream_property_packet_to_frame() is not None
        assert capi.get_audio_file_stream_property_frame_to_packet() is not None
        assert capi.get_audio_file_stream_property_packet_to_byte() is not None
        assert capi.get_audio_file_stream_property_byte_to_packet() is not None
        assert capi.get_audio_file_stream_property_packet_table_info() is not None
        assert capi.get_audio_file_stream_property_packet_size_upper_bound() is not None
        assert (
            capi.get_audio_file_stream_property_average_bytes_per_packet() is not None
        )
        assert capi.get_audio_file_stream_property_bit_rate() is not None
        assert capi.get_audio_file_stream_property_info_dictionary() is not None

    def test_audio_file_stream_flag_constants(self):
        """Test AudioFileStream flag constants"""
        assert capi.get_audio_file_stream_property_flag_property_is_cached() is not None
        assert capi.get_audio_file_stream_property_flag_cache_property() is not None
        assert capi.get_audio_file_stream_parse_flag_discontinuity() is not None
        assert capi.get_audio_file_stream_seek_flag_offset_is_estimated() is not None

    def test_audio_file_stream_error_constants(self):
        """Test AudioFileStream error constants"""
        assert capi.get_audio_file_stream_error_unsupported_file_type() is not None
        assert capi.get_audio_file_stream_error_unsupported_data_format() is not None
        assert capi.get_audio_file_stream_error_unsupported_property() is not None
        assert capi.get_audio_file_stream_error_bad_property_size() is not None
        assert capi.get_audio_file_stream_error_not_optimized() is not None
        assert capi.get_audio_file_stream_error_invalid_packet_offset() is not None
        assert capi.get_audio_file_stream_error_invalid_file() is not None
        assert capi.get_audio_file_stream_error_value_unknown() is not None
        assert capi.get_audio_file_stream_error_data_unavailable() is not None
        assert capi.get_audio_file_stream_error_illegal_operation() is not None
        assert capi.get_audio_file_stream_error_unspecified_error() is not None
        assert capi.get_audio_file_stream_error_discontinuity_cant_recover() is not None


class TestAudioFileStreamBasicOperations:
    """Test basic AudioFileStream operations"""

    def test_audio_file_stream_open_close(self):
        """Test opening and closing an AudioFileStream"""
        stream_id = capi.audio_file_stream_open()
        assert isinstance(stream_id, int)
        assert stream_id != 0
        result = capi.audio_file_stream_close(stream_id)
        assert result == 0
        wav_type = capi.get_audio_file_wave_type()
        stream_id = capi.audio_file_stream_open(wav_type)
        assert isinstance(stream_id, int)
        assert stream_id != 0
        result = capi.audio_file_stream_close(stream_id)
        assert result == 0

    def test_audio_file_stream_parse_empty_data(self):
        """Test parsing empty data through AudioFileStream"""
        stream_id = capi.audio_file_stream_open()
        try:
            result = capi.audio_file_stream_parse_bytes(stream_id, b"")
            assert result == 0
            discontinuity_flag = capi.get_audio_file_stream_parse_flag_discontinuity()
            result = capi.audio_file_stream_parse_bytes(
                stream_id, b"", discontinuity_flag
            )
            assert result == 0
        finally:
            capi.audio_file_stream_close(stream_id)


class TestAudioFileStreamWithWaveFile:
    """Test AudioFileStream with real WAV file data"""

    @pytest.fixture
    def wav_file_path(self):
        """Path to test WAV file"""
        test_dir = os.path.dirname(os.path.abspath(__file__))
        wav_path = os.path.join(test_dir, "amen.wav")
        if not os.path.exists(wav_path):
            pytest.skip(f"Test WAV file not found: {wav_path}")
        return wav_path

    @pytest.fixture
    def wav_data(self, wav_file_path):
        """Load WAV file data"""
        with open(wav_file_path, "rb") as f:
            return f.read()

    def test_audio_file_stream_parse_wav_header(self, wav_data):
        """Test parsing WAV file header through AudioFileStream"""
        stream_id = capi.audio_file_stream_open(capi.get_audio_file_wave_type())
        try:
            header_chunk = wav_data[:1024]
            result = capi.audio_file_stream_parse_bytes(stream_id, header_chunk)
            assert result == 0
            try:
                ready = capi.audio_file_stream_get_property(
                    stream_id,
                    capi.get_audio_file_stream_property_ready_to_produce_packets(),
                )
                assert isinstance(ready, int)
            except RuntimeError:
                pass
            try:
                file_format = capi.audio_file_stream_get_property(
                    stream_id, capi.get_audio_file_stream_property_file_format()
                )
                assert isinstance(file_format, int)
                assert file_format == capi.get_audio_file_wave_type()
            except RuntimeError:
                pass
        finally:
            capi.audio_file_stream_close(stream_id)

    def test_audio_file_stream_incremental_parsing(self, wav_data):
        """Test incremental parsing of WAV file data"""
        stream_id = capi.audio_file_stream_open(capi.get_audio_file_wave_type())
        try:
            chunk_size = 512
            for i in range(0, min(len(wav_data), 4096), chunk_size):
                chunk = wav_data[i : i + chunk_size]
                if chunk:
                    result = capi.audio_file_stream_parse_bytes(stream_id, chunk)
                    assert result == 0
            try:
                file_format = capi.audio_file_stream_get_property(
                    stream_id, capi.get_audio_file_stream_property_file_format()
                )
                assert file_format == capi.get_audio_file_wave_type()
            except RuntimeError:
                pass
        finally:
            capi.audio_file_stream_close(stream_id)

    def test_audio_file_stream_data_format_property(self, wav_data):
        """Test getting data format property from parsed WAV data"""
        stream_id = capi.audio_file_stream_open(capi.get_audio_file_wave_type())
        try:
            result = capi.audio_file_stream_parse_bytes(stream_id, wav_data[:2048])
            assert result == 0
            try:
                data_format = capi.audio_file_stream_get_property(
                    stream_id, capi.get_audio_file_stream_property_data_format()
                )
                assert isinstance(data_format, dict)
                required_fields = [
                    "sample_rate",
                    "format_id",
                    "format_flags",
                    "bytes_per_packet",
                    "frames_per_packet",
                    "bytes_per_frame",
                    "channels_per_frame",
                    "bits_per_channel",
                    "reserved",
                ]
                for field in required_fields:
                    assert field in data_format
                    assert isinstance(data_format[field], (int, float))
                assert data_format["sample_rate"] > 0
                assert data_format["format_id"] == capi.get_audio_format_linear_pcm()
                assert data_format["channels_per_frame"] > 0
                assert data_format["bits_per_channel"] > 0
            except RuntimeError as e:
                print(f"Data format not yet available: {e}")
        finally:
            capi.audio_file_stream_close(stream_id)


class TestAudioFileStreamSeek:
    """Test AudioFileStream seek functionality"""

    def test_audio_file_stream_seek_operations(self):
        """Test seek operations on AudioFileStream"""
        stream_id = capi.audio_file_stream_open()
        try:
            try:
                seek_result = capi.audio_file_stream_seek(stream_id, 0)
                assert isinstance(seek_result, dict)
                assert "byte_offset" in seek_result
                assert "flags" in seek_result
                assert "is_estimated" in seek_result
                assert isinstance(seek_result["byte_offset"], int)
                assert isinstance(seek_result["flags"], int)
                assert isinstance(seek_result["is_estimated"], bool)
            except RuntimeError:
                pass
        finally:
            capi.audio_file_stream_close(stream_id)


class TestAudioFileStreamPropertyInfo:
    """Test AudioFileStream property info functionality"""

    def test_audio_file_stream_property_access_behavior(self):
        """Test property access behavior before parsing data"""
        stream_id = capi.audio_file_stream_open()
        try:
            ready = capi.audio_file_stream_get_property(
                stream_id,
                capi.get_audio_file_stream_property_ready_to_produce_packets(),
            )
            assert ready == 0
            with pytest.raises(RuntimeError):
                capi.audio_file_stream_get_property(
                    stream_id, capi.get_audio_file_stream_property_file_format()
                )
            with pytest.raises(RuntimeError):
                capi.audio_file_stream_get_property(
                    stream_id, capi.get_audio_file_stream_property_data_format()
                )
        finally:
            capi.audio_file_stream_close(stream_id)
