"""LLM integration mixin — async chat and event-loop management."""

from __future__ import annotations

import asyncio
import logging
import sys
import time
import warnings

from timmy.voice.speech_engines import _VOICE_PREAMBLE, _strip_markdown

logger = logging.getLogger(__name__)


class LLMMixin:
    """Mixin providing LLM chat methods for :class:`VoiceLoop`."""

    def _get_loop(self) -> asyncio.AbstractEventLoop:
        """Return a persistent event loop, creating one if needed."""
        if self._loop is None or self._loop.is_closed():
            self._loop = asyncio.new_event_loop()
        return self._loop

    def _think(self, user_text: str) -> str:
        """Send text to Timmy and get a response."""
        sys.stdout.write("  💭 Thinking...\r")
        sys.stdout.flush()
        t0 = time.monotonic()
        try:
            loop = self._get_loop()
            response = loop.run_until_complete(self._chat(user_text))
        except (ConnectionError, RuntimeError, ValueError) as exc:
            logger.error("Timmy chat failed: %s", exc)
            response = "I'm having trouble thinking right now. Could you try again?"
        elapsed = time.monotonic() - t0
        logger.info("Timmy responded in %.1fs", elapsed)
        response = _strip_markdown(response)
        return response

    async def _chat(self, message: str) -> str:
        """Async wrapper around Timmy's session.chat()."""
        from timmy.session import chat

        voiced = f"{_VOICE_PREAMBLE}\n\nUser said: {message}"
        return await chat(voiced, session_id=self.config.session_id)

    def _cleanup_loop(self) -> None:
        """Shut down the persistent event loop cleanly."""
        if self._loop is None or self._loop.is_closed():
            return

        self._loop.set_exception_handler(lambda loop, ctx: None)
        try:
            self._loop.run_until_complete(self._loop.shutdown_asyncgens())
        except RuntimeError as exc:
            logger.debug("Shutdown asyncgens failed: %s", exc)
            pass

        with warnings.catch_warnings():
            warnings.simplefilter("ignore", RuntimeWarning)
            try:
                self._loop.close()
            except RuntimeError as exc:
                logger.debug("Loop close failed: %s", exc)
                pass

        self._loop = None
