"""Tests for ExtendedAudioFile functional API."""

import os
import tempfile
import struct
import pytest
import coremusic as cm
import coremusic.capi as capi


class TestExtendedAudioFileAPI:
    """Test ExtendedAudioFile functional API"""

    @pytest.fixture
    def amen_wav_path(self):
        """Fixture providing path to amen.wav test file"""
        path = os.path.join("tests", "amen.wav")
        if not os.path.exists(path):
            pytest.skip(f"Test audio file not found: {path}")
        return path

    @pytest.fixture
    def temp_audio_file(self):
        """Fixture providing temporary audio file path"""
        fd, path = tempfile.mkstemp(suffix=".wav")
        os.close(fd)
        yield path
        if os.path.exists(path):
            try:
                os.unlink(path)
            except:
                pass

    @pytest.fixture
    def pcm_format_dict(self):
        """Fixture providing PCM audio format dictionary"""
        return {
            "sample_rate": 44100.0,
            "format_id": capi.get_audio_format_linear_pcm(),
            "format_flags": 12,
            "bytes_per_packet": 4,
            "frames_per_packet": 1,
            "bytes_per_frame": 4,
            "channels_per_frame": 2,
            "bits_per_channel": 16,
            "reserved": 0,
        }

    def test_extended_audio_file_open_url(self, amen_wav_path):
        """Test opening an existing audio file"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        assert ext_file_id is not None
        assert ext_file_id > 0
        capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_read(self, amen_wav_path):
        """Test reading frames from audio file"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        try:
            audio_data, frames_read = capi.extended_audio_file_read(ext_file_id, 1000)
            assert isinstance(audio_data, bytes)
            assert isinstance(frames_read, int)
            assert len(audio_data) > 0
            assert frames_read > 0
            assert frames_read <= 1000
        finally:
            capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_get_file_data_format(self, amen_wav_path):
        """Test getting file data format property"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        try:
            property_id = capi.get_extended_audio_file_property_file_data_format()
            format_data = capi.extended_audio_file_get_property(
                ext_file_id, property_id
            )
            assert isinstance(format_data, bytes)
            assert len(format_data) >= 40
            asbd = struct.unpack("<dLLLLLLLL", format_data[:40])
            (
                sample_rate,
                format_id,
                format_flags,
                bytes_per_packet,
                frames_per_packet,
                bytes_per_frame,
                channels_per_frame,
                bits_per_channel,
                reserved,
            ) = asbd
            assert sample_rate == 44100.0
            assert channels_per_frame == 2
            assert bits_per_channel == 16
        finally:
            capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_create_and_write(
        self, temp_audio_file, pcm_format_dict
    ):
        """Test creating and writing to a new audio file"""
        ext_file_id = capi.extended_audio_file_create_with_url(
            temp_audio_file, capi.get_audio_file_wave_type(), pcm_format_dict, 0
        )
        try:
            num_frames = 1000
            audio_data = bytes([(i % 256) for i in range(num_frames * 4)])
            capi.extended_audio_file_write(ext_file_id, num_frames, audio_data)
            assert os.path.exists(temp_audio_file)
            assert os.path.getsize(temp_audio_file) > 0
        finally:
            capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_set_client_data_format(
        self, amen_wav_path, pcm_format_dict
    ):
        """Test setting client data format for format conversion"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        try:
            property_id = capi.get_extended_audio_file_property_client_data_format()
            asbd_bytes = struct.pack(
                "<dLLLLLLLL",
                pcm_format_dict["sample_rate"],
                pcm_format_dict["format_id"],
                pcm_format_dict["format_flags"],
                pcm_format_dict["bytes_per_packet"],
                pcm_format_dict["frames_per_packet"],
                pcm_format_dict["bytes_per_frame"],
                pcm_format_dict["channels_per_frame"],
                pcm_format_dict["bits_per_channel"],
                pcm_format_dict["reserved"],
            )
            capi.extended_audio_file_set_property(ext_file_id, property_id, asbd_bytes)
            audio_data, frames_read = capi.extended_audio_file_read(ext_file_id, 100)
            assert len(audio_data) > 0
            assert frames_read > 0
        finally:
            capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_get_file_length_frames(self, amen_wav_path):
        """Test getting file length in frames"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        try:
            property_id = capi.get_extended_audio_file_property_file_length_frames()
            length_data = capi.extended_audio_file_get_property(
                ext_file_id, property_id
            )
            assert isinstance(length_data, bytes)
            assert len(length_data) >= 8
            frame_count = struct.unpack("<q", length_data[:8])[0]
            assert frame_count > 0
            assert frame_count > 100000
        finally:
            capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_property_getters(self):
        """Test ExtendedAudioFile property ID getter functions"""
        assert isinstance(capi.get_extended_audio_file_property_file_data_format(), int)
        assert isinstance(
            capi.get_extended_audio_file_property_file_channel_layout(), int
        )
        assert isinstance(
            capi.get_extended_audio_file_property_client_data_format(), int
        )
        assert isinstance(
            capi.get_extended_audio_file_property_client_channel_layout(), int
        )
        assert isinstance(
            capi.get_extended_audio_file_property_codec_manufacturer(), int
        )
        assert isinstance(capi.get_extended_audio_file_property_audio_file(), int)
        assert isinstance(
            capi.get_extended_audio_file_property_file_length_frames(), int
        )

    def test_extended_audio_file_read_multiple_chunks(self, amen_wav_path):
        """Test reading audio file in multiple chunks"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        try:
            total_frames_read = 0
            chunk_size = 1000
            audio_data1, frames_read1 = capi.extended_audio_file_read(
                ext_file_id, chunk_size
            )
            assert frames_read1 > 0
            total_frames_read += frames_read1
            audio_data2, frames_read2 = capi.extended_audio_file_read(
                ext_file_id, chunk_size
            )
            assert frames_read2 > 0
            total_frames_read += frames_read2
            audio_data3, frames_read3 = capi.extended_audio_file_read(
                ext_file_id, chunk_size
            )
            assert frames_read3 > 0
            total_frames_read += frames_read3
            assert total_frames_read > 0
        finally:
            capi.extended_audio_file_dispose(ext_file_id)

    def test_extended_audio_file_create_overwrite(
        self, temp_audio_file, pcm_format_dict
    ):
        """Test creating file with overwrite flag"""
        with open(temp_audio_file, "wb") as f:
            f.write(b"dummy data")
        assert os.path.exists(temp_audio_file)
        initial_size = os.path.getsize(temp_audio_file)
        ext_file_id = capi.extended_audio_file_create_with_url(
            temp_audio_file, capi.get_audio_file_wave_type(), pcm_format_dict, 0
        )
        try:
            num_frames = 100
            audio_data = b"\x00\x01\x02\x03" * num_frames
            capi.extended_audio_file_write(ext_file_id, num_frames, audio_data)
        finally:
            capi.extended_audio_file_dispose(ext_file_id)
        assert os.path.exists(temp_audio_file)
        final_size = os.path.getsize(temp_audio_file)
        assert final_size != initial_size

    def test_extended_audio_file_error_handling(self):
        """Test ExtendedAudioFile error handling"""
        with pytest.raises((RuntimeError, cm.CoreAudioError)):
            capi.extended_audio_file_open_url("/nonexistent/path/to/file.wav")
        with pytest.raises((RuntimeError, cm.CoreAudioError)):
            capi.extended_audio_file_read(999999, 100)

    def test_extended_audio_file_write_then_read(
        self, temp_audio_file, pcm_format_dict
    ):
        """Test write-then-read round-trip"""
        ext_file_id = capi.extended_audio_file_create_with_url(
            temp_audio_file, capi.get_audio_file_wave_type(), pcm_format_dict, 0
        )
        num_frames = 500
        test_pattern = bytes([(i * 13 % 256) for i in range(num_frames * 4)])
        try:
            capi.extended_audio_file_write(ext_file_id, num_frames, test_pattern)
        finally:
            capi.extended_audio_file_dispose(ext_file_id)
        ext_file_id_read = capi.extended_audio_file_open_url(temp_audio_file)
        try:
            audio_data, frames_read = capi.extended_audio_file_read(
                ext_file_id_read, num_frames
            )
            assert frames_read == num_frames
            assert len(audio_data) > 0
            assert len(audio_data) == len(test_pattern)
        finally:
            capi.extended_audio_file_dispose(ext_file_id_read)

    def test_extended_audio_file_read_entire_file(self, amen_wav_path):
        """Test reading entire audio file"""
        ext_file_id = capi.extended_audio_file_open_url(amen_wav_path)
        try:
            property_id = capi.get_extended_audio_file_property_file_length_frames()
            length_data = capi.extended_audio_file_get_property(
                ext_file_id, property_id
            )
            total_frames = struct.unpack("<q", length_data[:8])[0]
            audio_data, frames_read = capi.extended_audio_file_read(
                ext_file_id, total_frames
            )
            assert frames_read > 0
            assert frames_read <= total_frames
            assert len(audio_data) > 0
        finally:
            capi.extended_audio_file_dispose(ext_file_id)
