from __future__ import annotations

import asyncio
import os
import time
import uuid
from typing import Any, Callable, List

from livekit.agents.stt.stt import (
    STT,
    STTCapabilities,
    SpeechData,
    SpeechEvent,
    SpeechEventType,
)
from livekit.agents.types import APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS, NOT_GIVEN, NotGivenOr
from livekit.agents.utils.audio import AudioBuffer, calculate_audio_duration

from app.utils.logging import (
    EVENT_RECEBIMENTO_MSG,
    event_latency_ms,
    log_structured_event,
    setup_minimal_logging,
)
from app.utils.turn_ids import (
    next_turn_message_id,
    register_started_turn_message_id,
    register_transcribed_turn,
)

logger = setup_minimal_logging()


def _parse_transcripts(raw_value: str) -> List[str]:
    parts: List[str] = []
    for chunk in (raw_value or "").replace("\n", "|").split("|"):
        text = chunk.strip()
        if text:
            parts.append(text)
    return parts


class FakeSTT(STT):
    def __init__(
        self,
        *,
        language: str = "pt-BR",
        timeline: Any | None = None,
        structured_log_context: Any | None = None,
        structured_logger: Any | None = None,
        structured_interruption_flag: Callable[[], bool] | None = None,
    ) -> None:
        super().__init__(capabilities=STTCapabilities(streaming=False, interim_results=False))
        self._language = (language or "pt-BR").strip() or "pt-BR"
        self._timeline = timeline
        self._structured_log_context = structured_log_context
        self._structured_logger = structured_logger
        self._structured_interruption_flag = structured_interruption_flag
        self._transcripts = _parse_transcripts(
            os.getenv(
                "FAKE_STT_TRANSCRIPTS",
                "alô|quero seguir com a simulacao|sim|obrigado, tchau",
            )
        )
        if not self._transcripts:
            self._transcripts = ["alô", "sim", "obrigado, tchau"]
        self._mode = (os.getenv("FAKE_STT_MODE", "repeat_last") or "repeat_last").strip().lower()
        self._min_audio_ms = max(0, int(os.getenv("FAKE_STT_MIN_AUDIO_MS", "150") or "150"))
        self._cursor = 0
        self._lock = asyncio.Lock()

    def _structured_interruption_value(self) -> int:
        if self._structured_interruption_flag is None:
            return 0
        try:
            return 1 if bool(self._structured_interruption_flag()) else 0
        except Exception:
            return 0

    @property
    def model(self) -> str:
        return "fake-script"

    @property
    def provider(self) -> str:
        return "fake"

    async def _recognize_impl(
        self,
        buffer: AudioBuffer,
        *,
        language: NotGivenOr[str] = NOT_GIVEN,
        conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,
    ) -> SpeechEvent:
        del conn_options

        started_ns = time.time_ns()
        request_id = uuid.uuid4().hex
        audio_duration_ms = round(calculate_audio_duration(buffer) * 1000)
        resolved_language = language if isinstance(language, str) and language.strip() else self._language

        if audio_duration_ms < self._min_audio_ms:
            if self._timeline is not None:
                self._timeline.emit(
                    "stt_fake_skipped",
                    request_id=request_id,
                    duration_ms=audio_duration_ms,
                    reason="too_short",
                )
            return SpeechEvent(
                type=SpeechEventType.FINAL_TRANSCRIPT,
                request_id=request_id,
                alternatives=[SpeechData(language=resolved_language, text="", confidence=0.0)],
            )

        async with self._lock:
            current_index = self._cursor
            if self._mode == "cycle":
                transcript = self._transcripts[current_index % len(self._transcripts)]
                self._cursor += 1
            else:
                bounded_index = min(current_index, len(self._transcripts) - 1)
                transcript = self._transcripts[bounded_index]
                if self._cursor < len(self._transcripts) - 1:
                    self._cursor += 1

        if transcript.lower() in {"__silence__", "__empty__"}:
            transcript = ""

        if self._timeline is not None:
            self._timeline.emit(
                "stt_fake_completed",
                request_id=request_id,
                duration_ms=audio_duration_ms,
                transcript=transcript,
                transcript_index=current_index,
            )
        if transcript:
            finished_ns = time.time_ns()
            logger.info(
                "[stt][fake_transcript] req_id=%s duration_ms=%s audio_duration_ms=%s text=%r",
                request_id,
                event_latency_ms(started_ns, finished_ns),
                audio_duration_ms,
                transcript,
            )
        message_id = ""
        if transcript:
            message_id = next_turn_message_id(self._structured_log_context)
            register_started_turn_message_id(self._structured_log_context, message_id)
            register_transcribed_turn(
                self._structured_log_context,
                message_id=message_id,
                transcription=transcript,
                text=transcript,
            )

        if transcript and self._structured_logger is not None:
            total_ms = event_latency_ms(started_ns, finished_ns)
            log_structured_event(
                self._structured_logger,
                self._structured_log_context,
                tipo_evento=EVENT_RECEBIMENTO_MSG,
                message_id=message_id,
                inicio_ns=started_ns,
                fim_ns=finished_ns,
                latencia_total_ms=total_ms,
                latencia_tffb_ms=total_ms,
                interrupcao=self._structured_interruption_value(),
            )

        return SpeechEvent(
            type=SpeechEventType.FINAL_TRANSCRIPT,
            request_id=request_id,
            alternatives=[SpeechData(language=resolved_language, text=transcript, confidence=1.0)],
        )

    async def aclose(self) -> None:
        return None
