"""Config flow for the Google Cloud integration."""

from __future__ import annotations

import json
import logging
from typing import TYPE_CHECKING, Any, cast

from google.cloud import texttospeech
import voluptuous as vol

from homeassistant.components.file_upload import process_uploaded_file
from homeassistant.components.tts import CONF_LANG
from homeassistant.config_entries import (
    ConfigEntry,
    ConfigFlow,
    ConfigFlowResult,
    OptionsFlowWithReload,
)
from homeassistant.core import callback
from homeassistant.helpers.selector import (
    FileSelector,
    FileSelectorConfig,
    SelectSelector,
    SelectSelectorConfig,
    SelectSelectorMode,
)

from .const import (
    CONF_KEY_FILE,
    CONF_SERVICE_ACCOUNT_INFO,
    CONF_STT_MODEL,
    DEFAULT_LANG,
    DEFAULT_STT_MODEL,
    DOMAIN,
    SUPPORTED_STT_MODELS,
    TITLE,
)
from .helpers import (
    async_tts_voices,
    tts_options_schema,
    tts_platform_schema,
    validate_service_account_info,
)

_LOGGER = logging.getLogger(__name__)

UPLOADED_KEY_FILE = "uploaded_key_file"

STEP_USER_DATA_SCHEMA = vol.Schema(
    {
        vol.Required(UPLOADED_KEY_FILE): FileSelector(
            FileSelectorConfig(accept=".json,application/json")
        )
    }
)


class GoogleCloudConfigFlow(ConfigFlow, domain=DOMAIN):
    """Handle a config flow for Google Cloud integration."""

    VERSION = 1

    _name: str | None = None
    entry: ConfigEntry | None = None
    abort_reason: str | None = None

    def _parse_uploaded_file(self, uploaded_file_id: str) -> dict[str, Any]:
        """Read and parse an uploaded JSON file."""
        with process_uploaded_file(self.hass, uploaded_file_id) as file_path:
            contents = file_path.read_text()
        return cast(dict[str, Any], json.loads(contents))

    async def async_step_user(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Handle the initial step."""
        errors: dict[str, Any] = {}
        if user_input is not None:
            try:
                service_account_info = await self.hass.async_add_executor_job(
                    self._parse_uploaded_file, user_input[UPLOADED_KEY_FILE]
                )
                validate_service_account_info(service_account_info)
            except ValueError:
                _LOGGER.exception("Reading uploaded JSON file failed")
                errors["base"] = "invalid_file"
            else:
                data = {CONF_SERVICE_ACCOUNT_INFO: service_account_info}
                if self.entry:
                    if TYPE_CHECKING:
                        assert self.abort_reason
                    return self.async_update_reload_and_abort(
                        self.entry, data=data, reason=self.abort_reason
                    )
                return self.async_create_entry(title=TITLE, data=data)
        return self.async_show_form(
            step_id="user",
            data_schema=STEP_USER_DATA_SCHEMA,
            errors=errors,
            description_placeholders={
                "url": "https://console.cloud.google.com/apis/credentials/serviceaccountkey"
            },
        )

    async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
        """Import Google Cloud configuration from YAML."""

        def _read_key_file() -> dict[str, Any]:
            with open(
                self.hass.config.path(import_data[CONF_KEY_FILE]), encoding="utf8"
            ) as f:
                return cast(dict[str, Any], json.load(f))

        service_account_info = await self.hass.async_add_executor_job(_read_key_file)
        try:
            validate_service_account_info(service_account_info)
        except ValueError:
            _LOGGER.exception("Reading credentials JSON file failed")
            return self.async_abort(reason="invalid_file")
        options = {
            k: v for k, v in import_data.items() if k in tts_platform_schema().schema
        }
        options.pop(CONF_KEY_FILE)
        _LOGGER.debug("Creating imported config entry with options: %s", options)
        return self.async_create_entry(
            title=TITLE,
            data={CONF_SERVICE_ACCOUNT_INFO: service_account_info},
            options=options,
        )

    @staticmethod
    @callback
    def async_get_options_flow(
        config_entry: ConfigEntry,
    ) -> GoogleCloudOptionsFlowHandler:
        """Create the options flow."""
        return GoogleCloudOptionsFlowHandler()


class GoogleCloudOptionsFlowHandler(OptionsFlowWithReload):
    """Google Cloud options flow."""

    async def async_step_init(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Manage the options."""
        if user_input is not None:
            return self.async_create_entry(data=user_input)

        service_account_info = self.config_entry.data[CONF_SERVICE_ACCOUNT_INFO]
        client: texttospeech.TextToSpeechAsyncClient = (
            texttospeech.TextToSpeechAsyncClient.from_service_account_info(
                service_account_info
            )
        )
        voices = await async_tts_voices(client)
        return self.async_show_form(
            step_id="init",
            data_schema=self.add_suggested_values_to_schema(
                vol.Schema(
                    {
                        vol.Optional(
                            CONF_LANG,
                            default=DEFAULT_LANG,
                        ): SelectSelector(
                            SelectSelectorConfig(
                                mode=SelectSelectorMode.DROPDOWN, options=list(voices)
                            )
                        ),
                        **tts_options_schema(
                            self.config_entry.options, voices, from_config_flow=True
                        ).schema,
                        vol.Optional(
                            CONF_STT_MODEL,
                            default=DEFAULT_STT_MODEL,
                        ): SelectSelector(
                            SelectSelectorConfig(
                                mode=SelectSelectorMode.DROPDOWN,
                                options=SUPPORTED_STT_MODELS,
                            )
                        ),
                    }
                ),
                self.config_entry.options,
            ),
        )
