"""ASR segment processing for streaming transcription."""

from __future__ import annotations

from collections.abc import AsyncIterator
from typing import TYPE_CHECKING, Protocol, cast

import numpy as np
from numpy.typing import NDArray

from noteflow.domain.entities import Segment
from noteflow.infrastructure.logging import get_logger

from ...proto import noteflow_pb2
from ..converters import (
    create_segment_from_asr,
    parse_meeting_id_or_none,
    segment_to_proto_update,
)

if TYPE_CHECKING:
    from ..protocols import ServicerHost

logger = get_logger(__name__)


class _SpeakerAssignable(Protocol):
    def maybe_assign_speaker(self, meeting_id: str, segment: Segment) -> None: ...


async def process_audio_segment(
    host: ServicerHost,
    meeting_id: str,
    audio: NDArray[np.float32],
    segment_start_time: float,
) -> AsyncIterator[noteflow_pb2.TranscriptUpdate]:
    """Process a complete audio segment through ASR.

    Uses unified repository provider for both DB and memory backends.
    Batches all segments from ASR results and commits once per audio chunk.

    Args:
        host: The servicer host.
        meeting_id: Meeting identifier.
        audio: Complete audio segment.
        segment_start_time: Segment start time in stream seconds.

    Yields:
        TranscriptUpdates for transcribed segments.
    """
    if len(audio) == 0 or host.asr_engine is None:
        return

    parsed_meeting_id = parse_meeting_id_or_none(meeting_id)
    if parsed_meeting_id is None:
        logger.warning("Invalid meeting_id %s in streaming segment", meeting_id)
        return

    async with host.create_repository_provider() as repo:
        meeting = await repo.meetings.get(parsed_meeting_id)
        if meeting is None:
            return

        results = await host.asr_engine.transcribe_async(audio)

        # Build all segments first
        segments_to_add: list[tuple[Segment, noteflow_pb2.TranscriptUpdate]] = []
        for result in results:
            segment_id = host.next_segment_id(
                meeting_id,
                fallback=meeting.next_segment_id,
            )
            segment = create_segment_from_asr(
                meeting.id,
                segment_id,
                result,
                segment_start_time,
            )
            # Call diarization mixin method if available
            if hasattr(host, "maybe_assign_speaker"):
                cast(_SpeakerAssignable, host).maybe_assign_speaker(meeting_id, segment)
            await repo.segments.add(meeting.id, segment)
            segments_to_add.append((segment, segment_to_proto_update(meeting_id, segment)))

        # Single commit for all segments in this audio chunk
        if segments_to_add:
            await repo.commit()

        # Yield updates after commit
        for _, update in segments_to_add:
            yield update

        # Decrement pending chunks counter after processing (for congestion tracking)
        # Lazy import to avoid circular import with _processing.py
        from ._processing import decrement_pending_chunks

        decrement_pending_chunks(host, meeting_id)
