"""Integration test for real VAD + Segmenter streaming pipeline."""

from __future__ import annotations

from collections.abc import AsyncIterator
from pathlib import Path
from typing import TYPE_CHECKING
from unittest.mock import AsyncMock, MagicMock

import grpc
import numpy as np
import numpy.typing as npt
import pytest

from noteflow.config.constants import DEFAULT_SAMPLE_RATE
from noteflow.domain.entities import Meeting
from noteflow.grpc.proto import noteflow_pb2
from noteflow.grpc.service import NoteFlowServicer
from noteflow.infrastructure.asr.dto import AsrResult
from noteflow.infrastructure.persistence.unit_of_work import SqlAlchemyUnitOfWork

if TYPE_CHECKING:
    from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

SAMPLE_RATE = DEFAULT_SAMPLE_RATE
CHUNK_SAMPLES = 1600  # 0.1s at 16kHz
SPEECH_CHUNKS = 4
SILENCE_CHUNKS = 10
EXPECTED_TEXT = "Real pipeline segment"


class MockContext:
    """Minimal gRPC context for integration streaming tests."""

    async def abort(self, code: grpc.StatusCode, details: str) -> None:
        _ = (code, details)
        raise grpc.RpcError()


def _make_chunk(meeting_id: str, audio: npt.NDArray[np.float32]) -> noteflow_pb2.AudioChunk:
    """Create a protobuf audio chunk."""
    return noteflow_pb2.AudioChunk(
        meeting_id=meeting_id,
        audio_data=audio.astype(np.float32).tobytes(),
        sample_rate=SAMPLE_RATE,
        channels=1,
    )


async def _audio_stream(meeting_id: str) -> AsyncIterator[noteflow_pb2.AudioChunk]:
    """Yield speech then silence chunks to exercise VAD + Segmenter."""
    rng = np.random.default_rng(0)
    speech = rng.uniform(-0.2, 0.2, CHUNK_SAMPLES).astype(np.float32)
    silence = np.zeros(CHUNK_SAMPLES, dtype=np.float32)

    for _ in range(SPEECH_CHUNKS):
        yield _make_chunk(meeting_id, speech)

    for _ in range(SILENCE_CHUNKS):
        yield _make_chunk(meeting_id, silence)


@pytest.mark.integration
class TestStreamingRealPipeline:
    """Validate streaming with real VAD + Segmenter path."""

    @pytest.mark.asyncio
    async def test_streaming_emits_final_segment(
        self,
        session_factory: async_sessionmaker[AsyncSession],
        meetings_dir: Path,
    ) -> None:
        """Real VAD + Segmenter should emit at least one final segment."""
        async with SqlAlchemyUnitOfWork(session_factory, meetings_dir) as uow:
            meeting = Meeting.create(title="Real pipeline")
            await uow.meetings.create(meeting)
            await uow.commit()

        mock_asr = MagicMock()
        mock_asr.is_loaded = True
        mock_asr.transcribe_async = AsyncMock(
            return_value=[AsrResult(text=EXPECTED_TEXT, start=0.0, end=0.5)]
        )

        servicer = NoteFlowServicer(
            session_factory=session_factory,
            asr_engine=mock_asr,
            meetings_dir=meetings_dir,
        )

        updates: list[noteflow_pb2.TranscriptUpdate] = [
            update
            async for update in servicer.StreamTranscription(
                _audio_stream(str(meeting.id)),
                MockContext(),
            )
        ]

        final_updates: list[noteflow_pb2.TranscriptUpdate] = [
            update
            for update in updates
            if update.update_type == noteflow_pb2.UPDATE_TYPE_FINAL
        ]
        assert final_updates, "Expected at least one final transcript update"

        async with SqlAlchemyUnitOfWork(session_factory, meetings_dir) as uow:
            segments = await uow.segments.get_by_meeting(meeting.id)
            segment_texts = {segment.text for segment in segments}

        assert EXPECTED_TEXT in segment_texts, "Expected segment text not persisted"
