from __future__ import annotations

from collections.abc import AsyncIterator

import pytest

from easycat.audio_format import PCM16_MONO_16K, AudioChunk
from easycat.events import Event, VADStartSpeaking, VADStopSpeaking
from easycat.testing import VADProviderContractSuite
from tests.contracts.provider_surface_matrix import PROVIDER_SURFACE_CONTRACTS

pytestmark = [pytest.mark.contract, pytest.mark.surface_vad, pytest.mark.provider("offline-fake")]


class _ContractVAD:
    def __init__(self) -> None:
        self.configured: dict[str, object] = {}
        self.processed = 0

    async def process(self, chunk: AudioChunk) -> AsyncIterator[Event]:
        self.processed += 1
        assert chunk.format == PCM16_MONO_16K
        yield VADStartSpeaking()
        yield VADStopSpeaking()

    def configure(
        self,
        *,
        min_speech_duration_ms: int = 250,
        min_silence_duration_ms: int = 150,
        sensitivity: float = 0.5,
    ) -> None:
        self.configured = {
            "min_speech_duration_ms": min_speech_duration_ms,
            "min_silence_duration_ms": min_silence_duration_ms,
            "sensitivity": sensitivity,
        }

    def version_info(self) -> dict[str, str]:
        return {
            "provider": "contract-vad",
            "model": "offline",
            "api_version": "v1",
            "sdk_version": "none",
        }


def test_vad_provider_contract_matrix_has_rows() -> None:
    rows = [row for row in PROVIDER_SURFACE_CONTRACTS if row.surface == "vad"]

    assert {row.provider for row in rows} == {"silero", "funasr", "ten", "krisp"}
    assert all(
        row.contract_path == "tests/contracts/test_vad_provider_contracts.py" for row in rows
    )


class TestVADContractSuite(VADProviderContractSuite):
    """Run the shipped provider-author kit suite against the offline fake.

    The protocol-semantics assertions live in
    :class:`easycat.testing.VADProviderContractSuite` so this file and the
    installable kit cannot drift; only fake-specific bookkeeping checks are
    added below.
    """

    provider_factory = _ContractVAD

    async def test_fake_records_configuration_and_boundary_events(
        self, provider: _ContractVAD
    ) -> None:
        chunk = AudioChunk(data=b"\0" * 320, format=PCM16_MONO_16K)

        provider.configure(sensitivity=0.7)
        events = [event async for event in provider.process(chunk)]

        assert provider.configured["sensitivity"] == 0.7
        assert provider.processed == 1
        assert [type(event) for event in events] == [VADStartSpeaking, VADStopSpeaking]
