from __future__ import annotations

import asyncio
from collections.abc import Callable

from .playground import Playground

QuickMessageHandler = Callable[[str, str], None]


class QuickChat:
    """Super simple chat wrapper for beginners.

    This class intentionally hides async details so a new user can go from
    install to first message in under a minute.
    """

    def __init__(
        self,
        room: str,
        *,
        port: int = 7337,
        open_port: bool = True,
        prompt: str = "> ",
        quit_words: tuple[str, ...] = ("quit", "exit"),
    ) -> None:
        self.room = room
        self.port = port
        self.open_port = open_port
        self.prompt = prompt
        self.quit_words = set(quit_words)
        self._pg = Playground(app_name=room, port=port)
        self._on_message: QuickMessageHandler | None = None
        self._pending_connect: tuple[str, int] | None = None
        self._show_nat_info = True

    @property
    def peer_id(self) -> str:
        return self._pg.peer_id

    def on_message(self, handler: QuickMessageHandler) -> None:
        self._on_message = handler

    def connect(self, host: str, port: int | None = None) -> None:
        self._pending_connect = (host, port or self.port)

    def show_nat_info(self, enabled: bool) -> None:
        self._show_nat_info = enabled

    def send(self, text: str) -> None:
        asyncio.run(self._pg.send_text(text))

    def run(self) -> None:
        asyncio.run(self._run_main())

    async def _run_main(self) -> None:
        nat = await self._pg.start(open_port=self.open_port)
        print("peer_id:", self._pg.peer_id)
        if self._show_nat_info and nat is not None:
            print("nat:", nat)

        self._pg.on("text", self._handle_text)

        if self._pending_connect is not None:
            host, port = self._pending_connect
            await self._pg.join(host, port)

        try:
            while True:
                text = await asyncio.to_thread(input, self.prompt)
                if text.strip().lower() in self.quit_words:
                    break
                await self._pg.send_text(text)
        finally:
            await self._pg.stop()

    def _handle_text(self, src: str, _kind: str, payload: dict[str, str]) -> None:
        text = payload.get("text", "")
        if self._on_message is None:
            print(f"[{src}] {text}")
            return
        self._on_message(src, text)
