"""Support for the Pico TTS speech service."""

import contextlib
import logging
import os
import shutil
import subprocess
import tempfile
from typing import Any, override

import voluptuous as vol

from homeassistant.components.tts import (
    CONF_LANG,
    PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
    Provider,
    TextToSpeechEntity,
    TtsAudioType,
)
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from .const import DEFAULT_LANG, DOMAIN, SUPPORT_LANGUAGES
from .issue import deprecate_yaml_issue

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = TTS_PLATFORM_SCHEMA.extend(
    {vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES)}
)


async def async_get_engine(
    hass: HomeAssistant,
    config: ConfigType,
    discovery_info: DiscoveryInfoType | None = None,
) -> Provider | None:
    """Set up Pico speech component."""
    if await hass.async_add_executor_job(shutil.which, "pico2wave") is None:
        _LOGGER.error("'pico2wave' was not found")
        return None

    hass.async_create_task(
        hass.config_entries.flow.async_init(
            DOMAIN, context={"source": SOURCE_IMPORT}, data=config
        )
    )

    deprecate_yaml_issue(hass)

    return PicoProvider(config[CONF_LANG])


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Pico TTS speech component via config entry."""
    async_add_entities([PicoTTSEntity(config_entry, config_entry.data[CONF_LANG])])


class PicoTTSEntity(TextToSpeechEntity):
    """The Pico TTS API entity."""

    _attr_supported_languages = SUPPORT_LANGUAGES

    def __init__(self, config_entry: ConfigEntry, lang: str) -> None:
        """Initialize Pico TTS service."""
        self._attr_default_language = lang
        self._attr_name = f"Pico TTS {lang}"
        self._attr_unique_id = config_entry.entry_id
        self._attr_device_info = DeviceInfo(
            entry_type=DeviceEntryType.SERVICE,
            identifiers={(DOMAIN, config_entry.entry_id)},
            model="Pico TTS",
            name=f"Pico TTS {lang}",
        )

    @override
    def get_tts_audio(
        self, message: str, language: str, options: dict[str, Any]
    ) -> TtsAudioType:
        """Load TTS using pico2wave."""
        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpf:
            fname = tmpf.name

        cmd = ["pico2wave", "--wave", fname, "-l", language]
        try:
            subprocess.run(cmd, text=True, input=message, check=True, timeout=30)
            with open(fname, "rb") as voice:
                data = voice.read()
        except subprocess.CalledProcessError as exc:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="returncode_error",
                translation_placeholders={"returncode": str(exc.returncode)},
            ) from exc
        except subprocess.TimeoutExpired as exc:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="timeout_error",
            ) from exc
        except OSError as exc:
            _LOGGER.debug("Full exception %s", exc)
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="file_read_error",
                translation_placeholders={"filename": fname},
            ) from exc
        finally:
            with contextlib.suppress(OSError):
                os.remove(fname)

        return "wav", data


class PicoProvider(Provider):
    """The Pico TTS API provider."""

    def __init__(self, lang: str) -> None:
        """Initialize Pico TTS provider."""
        self._lang = lang
        self.name = "PicoTTS"

    @property
    @override
    def default_language(self) -> str:
        """Return the default language."""
        return self._lang

    @property
    @override
    def supported_languages(self) -> list[str]:
        """Return list of supported languages."""
        return SUPPORT_LANGUAGES

    @override
    def get_tts_audio(
        self, message: str, language: str, options: dict[str, Any]
    ) -> TtsAudioType:
        """Load TTS using pico2wave."""
        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpf:
            fname = tmpf.name

        cmd = ["pico2wave", "--wave", fname, "-l", language]
        result = subprocess.run(cmd, text=True, input=message, check=False)
        data = None
        try:
            if result.returncode != 0:
                _LOGGER.error(
                    "Error running pico2wave, return code: %s", result.returncode
                )
                return None, None
            with open(fname, "rb") as voice:
                data = voice.read()
        except OSError:
            _LOGGER.error("Error trying to read %s", fname)
            return None, None
        finally:
            os.remove(fname)

        if data:
            return ("wav", data)
        return None, None
