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

import logging
from typing import Any, Dict, cast

import voluptuous as vol
from voluptuous.validators import All, Range
from yarl import URL

from homeassistant import config_entries
from homeassistant.const import CONF_URL
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_create_clientsession

from .api import FrigateApiClient, FrigateApiClientError
from .const import (
    CONF_ENABLE_WEBRTC,
    CONF_MEDIA_BROWSER_ENABLE,
    CONF_NOTIFICATION_PROXY_ENABLE,
    CONF_NOTIFICATION_PROXY_EXPIRE_AFTER_SECONDS,
    CONF_RTMP_URL_TEMPLATE,
    CONF_RTSP_URL_TEMPLATE,
    DEFAULT_HOST,
    DOMAIN,
)

_LOGGER: logging.Logger = logging.getLogger(__name__)


def get_config_entry_title(url_str: str) -> str:
    """Get the title of a config entry from the URL."""

    # Strip the scheme from the URL as it's not that interesting in the title
    # and space is limited on the integrations page.
    url = URL(url_str)
    return str(url)[len(url.scheme + "://") :]


class FrigateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):  # type: ignore[call-arg,misc]
    """Config flow for Frigate."""

    VERSION = 2
    CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH

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

        if user_input is None:
            return self._show_config_form()

        try:
            # Cannot use cv.url validation in the schema itself, so
            # apply extra validation here.
            cv.url(user_input[CONF_URL])
        except vol.Invalid:
            return self._show_config_form(user_input, errors={"base": "invalid_url"})

        try:
            session = async_create_clientsession(self.hass)
            client = FrigateApiClient(user_input[CONF_URL], session)
            await client.async_get_stats()
        except FrigateApiClientError:
            return self._show_config_form(user_input, errors={"base": "cannot_connect"})

        # Search for duplicates with the same Frigate CONF_HOST value.
        for existing_entry in self._async_current_entries(include_ignore=False):
            if existing_entry.data.get(CONF_URL) == user_input[CONF_URL]:
                return cast(
                    Dict[str, Any], self.async_abort(reason="already_configured")
                )

        return cast(
            Dict[str, Any],
            self.async_create_entry(
                title=get_config_entry_title(user_input[CONF_URL]), data=user_input
            ),
        )

    def _show_config_form(
        self,
        user_input: dict[str, Any] | None = None,
        errors: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Show the configuration form."""
        if user_input is None:
            user_input = {}

        return cast(
            Dict[str, Any],
            self.async_show_form(
                step_id="user",
                data_schema=vol.Schema(
                    {
                        vol.Required(
                            CONF_URL, default=user_input.get(CONF_URL, DEFAULT_HOST)
                        ): str
                    }
                ),
                errors=errors,
            ),
        )

    @staticmethod
    @callback  # type: ignore[misc]
    def async_get_options_flow(
        config_entry: config_entries.ConfigEntry,
    ) -> FrigateOptionsFlowHandler:
        """Get the Frigate Options flow."""
        return FrigateOptionsFlowHandler(config_entry)


class FrigateOptionsFlowHandler(config_entries.OptionsFlow):  # type: ignore[misc]
    """Frigate options flow."""

    def __init__(self, config_entry: config_entries.ConfigEntry):
        """Initialize a Frigate options flow."""
        self._config_entry = config_entry

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

        if not self.show_advanced_options:
            return cast(
                Dict[str, Any], self.async_abort(reason="only_advanced_options")
            )

        schema: dict[Any, Any] = {
            # Whether to enable webrtc as the medium for camera streaming
            vol.Optional(
                CONF_ENABLE_WEBRTC,
                default=self._config_entry.options.get(
                    CONF_ENABLE_WEBRTC,
                    False,
                ),
            ): bool,
            # The input URL is not validated as being a URL to allow for the
            # possibility the template input won't be a valid URL until after
            # it's rendered.
            vol.Optional(
                CONF_RTMP_URL_TEMPLATE,
                default=self._config_entry.options.get(
                    CONF_RTMP_URL_TEMPLATE,
                    "",
                ),
            ): str,
            # The input URL is not validated as being a URL to allow for the
            # possibility the template input won't be a valid URL until after
            # it's rendered.
            vol.Optional(
                CONF_RTSP_URL_TEMPLATE,
                default=self._config_entry.options.get(
                    CONF_RTSP_URL_TEMPLATE,
                    "",
                ),
            ): str,
            vol.Optional(
                CONF_NOTIFICATION_PROXY_ENABLE,
                default=self._config_entry.options.get(
                    CONF_NOTIFICATION_PROXY_ENABLE,
                    True,
                ),
            ): bool,
            vol.Optional(
                CONF_MEDIA_BROWSER_ENABLE,
                default=self._config_entry.options.get(
                    CONF_MEDIA_BROWSER_ENABLE,
                    True,
                ),
            ): bool,
            vol.Optional(
                CONF_NOTIFICATION_PROXY_EXPIRE_AFTER_SECONDS,
                default=self._config_entry.options.get(
                    CONF_NOTIFICATION_PROXY_EXPIRE_AFTER_SECONDS,
                    0,
                ),
            ): All(int, Range(min=0)),
        }

        return cast(
            Dict[str, Any],
            self.async_show_form(step_id="init", data_schema=vol.Schema(schema)),
        )
