"""Config flow for Philips TV integration."""
from __future__ import annotations

import platform
from typing import Any

from haphilipsjs import ConnectionFailure, PairingFailure, PhilipsTV
import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.const import (
    CONF_API_VERSION,
    CONF_HOST,
    CONF_PASSWORD,
    CONF_PIN,
    CONF_USERNAME,
)

from . import LOGGER
from .const import CONF_SYSTEM, CONST_APP_ID, CONST_APP_NAME, DOMAIN


async def validate_input(
    hass: core.HomeAssistant, host: str, api_version: int
) -> tuple[dict, PhilipsTV]:
    """Validate the user input allows us to connect."""
    hub = PhilipsTV(host, api_version)

    await hub.getSystem()
    await hub.setTransport(hub.secured_transport)

    if not hub.system:
        raise ConnectionFailure("System data is empty")

    return hub


class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
    """Handle a config flow for Philips TV."""

    VERSION = 1
    CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

    def __init__(self) -> None:
        """Initialize flow."""
        super().__init__()
        self._current = {}
        self._hub: PhilipsTV | None = None
        self._pair_state: Any = None

    async def async_step_import(self, conf: dict) -> dict:
        """Import a configuration from config.yaml."""
        for entry in self._async_current_entries():
            if entry.data[CONF_HOST] == conf[CONF_HOST]:
                return self.async_abort(reason="already_configured")

        return await self.async_step_user(
            {
                CONF_HOST: conf[CONF_HOST],
                CONF_API_VERSION: conf[CONF_API_VERSION],
            }
        )

    async def _async_create_current(self):

        system = self._current[CONF_SYSTEM]
        return self.async_create_entry(
            title=f"{system['name']} ({system['serialnumber']})",
            data=self._current,
        )

    async def async_step_pair(self, user_input: dict | None = None) -> dict:
        """Attempt to pair with device."""
        assert self._hub

        errors = {}
        schema = vol.Schema(
            {
                vol.Required(CONF_PIN): str,
            }
        )

        if not user_input:
            try:
                self._pair_state = await self._hub.pairRequest(
                    CONST_APP_ID,
                    CONST_APP_NAME,
                    platform.node(),
                    platform.system(),
                    "native",
                )
            except PairingFailure as exc:
                LOGGER.debug(exc)
                return self.async_abort(
                    reason="pairing_failure",
                    description_placeholders={"error_id": exc.data.get("error_id")},
                )
            return self.async_show_form(
                step_id="pair", data_schema=schema, errors=errors
            )

        try:
            username, password = await self._hub.pairGrant(
                self._pair_state, user_input[CONF_PIN]
            )
        except PairingFailure as exc:
            LOGGER.debug(exc)
            if exc.data.get("error_id") == "INVALID_PIN":
                errors[CONF_PIN] = "invalid_pin"
                return self.async_show_form(
                    step_id="pair", data_schema=schema, errors=errors
                )

            return self.async_abort(
                reason="pairing_failure",
                description_placeholders={"error_id": exc.data.get("error_id")},
            )

        self._current[CONF_USERNAME] = username
        self._current[CONF_PASSWORD] = password
        return await self._async_create_current()

    async def async_step_user(self, user_input: dict | None = None) -> dict:
        """Handle the initial step."""
        errors = {}
        if user_input:
            self._current = user_input
            try:
                hub = await validate_input(
                    self.hass, user_input[CONF_HOST], user_input[CONF_API_VERSION]
                )
            except ConnectionFailure as exc:
                LOGGER.error(exc)
                errors["base"] = "cannot_connect"
            except Exception:  # pylint: disable=broad-except
                LOGGER.exception("Unexpected exception")
                errors["base"] = "unknown"
            else:

                await self.async_set_unique_id(hub.system["serialnumber"])
                self._abort_if_unique_id_configured()

                self._current[CONF_SYSTEM] = hub.system
                self._current[CONF_API_VERSION] = hub.api_version
                self._hub = hub

                if hub.pairing_type == "digest_auth_pairing":
                    return await self.async_step_pair()
                return await self._async_create_current()

        schema = vol.Schema(
            {
                vol.Required(CONF_HOST, default=self._current.get(CONF_HOST)): str,
                vol.Required(
                    CONF_API_VERSION, default=self._current.get(CONF_API_VERSION, 1)
                ): vol.In([1, 5, 6]),
            }
        )
        return self.async_show_form(step_id="user", data_schema=schema, errors=errors)