"""Adds config flow for openHAB."""
from __future__ import annotations

from typing import Any

from homeassistant import config_entries
from homeassistant.config_entries import ConfigEntry, OptionsFlow
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
import voluptuous as vol

from .api import OpenHABApiClient
from .const import (
    AUTH_TYPES,
    CONF_AUTH_TOKEN,
    CONF_AUTH_TYPE,
    CONF_AUTH_TYPE_BASIC,
    CONF_AUTH_TYPE_TOKEN,
    CONF_BASE_URL,
    CONF_PASSWORD,
    CONF_USERNAME,
    DOMAIN,
    LOGGER,
    PLATFORMS,
)
from .utils import strip_ip


class OpenHABFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
    """Config flow for openHAB."""

    VERSION = 1

    def __init__(self) -> None:
        self._data: dict[str, Any] | None = None

    async def async_step_user(
        self,
        user_input: dict[str, Any] | None = None,
    ):
        """Handle a flow initialized by the user."""
        errors: dict[str, str] = {}

        LOGGER.info(user_input)

        if user_input is not None:
            self._data = user_input
            return await self.async_step_credentials(user_input)

        user_input = user_input or {}

        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema(
                {
                    vol.Required(
                        CONF_BASE_URL,
                        default=user_input.get(CONF_BASE_URL, "http://"),
                    ): str,
                    vol.Required(
                        CONF_AUTH_TYPE,
                        default=user_input.get(CONF_AUTH_TYPE, CONF_AUTH_TYPE_TOKEN),
                    ): vol.In(AUTH_TYPES),
                }
            ),
            errors=errors,
        )

    async def async_step_credentials(
        self,
        user_input: dict[str, Any] | None = None,
    ):
        """Handle the credentials step."""
        errors: dict[str, str] = {}

        if self._data is None:
            self._data = {}

        user_input = user_input or {}

        # Preserve values from first step
        user_input[CONF_BASE_URL] = self._data[CONF_BASE_URL]
        user_input[CONF_AUTH_TYPE] = self._data[CONF_AUTH_TYPE]

        # If credentials provided, test them
        if CONF_AUTH_TOKEN in user_input or CONF_USERNAME in user_input:
            if await self._test_credentials(
                user_input[CONF_BASE_URL],
                user_input[CONF_AUTH_TYPE],
                user_input.get(CONF_AUTH_TOKEN, ""),
                user_input.get(CONF_USERNAME, ""),
                user_input.get(CONF_PASSWORD, ""),
            ):
                return self.async_create_entry(
                    title=strip_ip(user_input[CONF_BASE_URL]),
                    data=user_input,
                )
            errors["base"] = "auth"

        # Build schema depending on auth type
        if user_input[CONF_AUTH_TYPE] == CONF_AUTH_TYPE_BASIC:
            schema = {
                vol.Optional(
                    CONF_USERNAME, default=user_input.get(CONF_USERNAME, "")
                ): cv.string,
                vol.Optional(
                    CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "")
                ): cv.string,
            }
        elif user_input[CONF_AUTH_TYPE] == CONF_AUTH_TYPE_TOKEN:
            schema = {
                vol.Required(
                    CONF_AUTH_TOKEN, default=user_input.get(CONF_AUTH_TOKEN, "")
                ): cv.string,
            }
        else:
            schema = {}

        return self.async_show_form(
            step_id="credentials",
            data_schema=vol.Schema(schema),
            errors=errors,
        )

    @staticmethod
    @callback
    def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
        """Return the options flow."""
        return OpenHABOptionsFlowHandler(config_entry)

    async def _test_credentials(
        self,
        base_url: str,
        auth_type: str,
        auth_token: str,
        username: str,
        password: str,
    ) -> bool:
        """Return true if credentials are valid."""
        client = OpenHABApiClient(
            self.hass,
            base_url,
            auth_type,
            auth_token,
            username,
            password,
        )
        await client.async_get_version()
        return True


class OpenHABOptionsFlowHandler(OptionsFlow):
    """openHAB config flow options handler."""

    def __init__(self, config_entry: ConfigEntry) -> None:
        """Initialize openHAB options flow."""
        self.config_entry = config_entry
        self._options: dict[str, Any] = dict(config_entry.options)

    async def async_step_init(self, user_input=None):
        """Manage the options."""
        return await self.async_step_user(user_input)

    async def async_step_user(self, user_input=None):
        """Handle options."""
        if user_input is not None:
            self._options.update(user_input)
            return self.async_create_entry(
                title=strip_ip(self.config_entry.data.get(CONF_BASE_URL)),
                data=self._options,
            )

        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema(
                {
                    vol.Required(
                        platform,
                        default=self._options.get(platform, True),
                    ): bool
                    for platform in sorted(PLATFORMS)
                }
            ),
        )

