"""Base class for Wyoming providers."""
from __future__ import annotations

import asyncio

import async_timeout
from wyoming.client import AsyncTcpClient
from wyoming.info import Describe, Info

from homeassistant.const import Platform

from .error import WyomingError

_INFO_TIMEOUT = 1
_INFO_RETRY_WAIT = 2
_INFO_RETRIES = 3


class WyomingService:
    """Hold info for Wyoming service."""

    def __init__(self, host: str, port: int, info: Info) -> None:
        """Initialize Wyoming service."""
        self.host = host
        self.port = port
        self.info = info
        platforms = []
        if any(asr.installed for asr in info.asr):
            platforms.append(Platform.STT)
        if any(tts.installed for tts in info.tts):
            platforms.append(Platform.TTS)
        self.platforms = platforms

    @classmethod
    async def create(cls, host: str, port: int) -> WyomingService | None:
        """Create a Wyoming service."""
        info = await load_wyoming_info(host, port)
        if info is None:
            return None

        return cls(host, port, info)


async def load_wyoming_info(
    host: str,
    port: int,
    retries: int = _INFO_RETRIES,
    retry_wait: float = _INFO_RETRY_WAIT,
    timeout: float = _INFO_TIMEOUT,
) -> Info | None:
    """Load info from Wyoming server."""
    wyoming_info: Info | None = None

    for _ in range(retries + 1):
        try:
            async with AsyncTcpClient(host, port) as client, async_timeout.timeout(
                timeout
            ):
                # Describe -> Info
                await client.write_event(Describe().event())
                while True:
                    event = await client.read_event()
                    if event is None:
                        raise WyomingError(
                            "Connection closed unexpectedly",
                        )

                    if Info.is_type(event.type):
                        wyoming_info = Info.from_event(event)
                        break  # while

                if wyoming_info is not None:
                    break  # for
        except (asyncio.TimeoutError, OSError, WyomingError):
            # Sleep and try again
            await asyncio.sleep(retry_wait)

    return wyoming_info
