"""Provides an HTTP API for mobile_app."""
import uuid
from typing import Dict

from aiohttp.web import Response, Request

from homeassistant.auth.util import generate_secret
from homeassistant.components.cloud import async_create_cloudhook
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.const import (HTTP_CREATED, HTTP_INTERNAL_SERVER_ERROR,
                                 CONF_WEBHOOK_ID)

from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.loader import get_component

from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID,
                    ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, CONF_SECRET,
                    CONF_USER_ID, DATA_REGISTRATIONS, DOMAIN,
                    ERR_INVALID_COMPONENT, ERR_SAVE_FAILURE,
                    REGISTRATION_SCHEMA)

from .helpers import error_response, supports_encryption, savable_state

from .webhook import setup_registration


def register_http_handlers(hass: HomeAssistantType, store: Store) -> bool:
    """Register the HTTP handlers/views."""
    hass.http.register_view(RegistrationsView(store))
    return True


class RegistrationsView(HomeAssistantView):
    """A view that accepts registration requests."""

    url = '/api/mobile_app/registrations'
    name = 'api:mobile_app:register'

    def __init__(self, store: Store) -> None:
        """Initialize the view."""
        self._store = store

    @RequestDataValidator(REGISTRATION_SCHEMA)
    async def post(self, request: Request, data: Dict) -> Response:
        """Handle the POST request for registration."""
        hass = request.app['hass']

        if ATTR_APP_COMPONENT in data:
            component = get_component(hass, data[ATTR_APP_COMPONENT])
            if component is None:
                fmt_str = "{} is not a valid component."
                msg = fmt_str.format(data[ATTR_APP_COMPONENT])
                return error_response(ERR_INVALID_COMPONENT, msg)

            if (hasattr(component, 'DEPENDENCIES') is False or
                    (hasattr(component, 'DEPENDENCIES') and
                     DOMAIN not in component.DEPENDENCIES)):
                fmt_str = "{} is not compatible with mobile_app."
                msg = fmt_str.format(data[ATTR_APP_COMPONENT])
                return error_response(ERR_INVALID_COMPONENT, msg)

        webhook_id = generate_secret()

        if hass.components.cloud.async_active_subscription():
            data[CONF_CLOUDHOOK_URL] = \
                await async_create_cloudhook(hass, webhook_id)

        data[ATTR_DEVICE_ID] = str(uuid.uuid4()).replace("-", "")

        data[CONF_WEBHOOK_ID] = webhook_id

        if data[ATTR_SUPPORTS_ENCRYPTION] and supports_encryption():
            from nacl.secret import SecretBox

            data[CONF_SECRET] = generate_secret(SecretBox.KEY_SIZE)

        data[CONF_USER_ID] = request['hass_user'].id

        hass.data[DOMAIN][DATA_REGISTRATIONS][webhook_id] = data

        try:
            await self._store.async_save(savable_state(hass))
        except HomeAssistantError:
            return error_response(ERR_SAVE_FAILURE,
                                  "Error saving registration",
                                  status=HTTP_INTERNAL_SERVER_ERROR)

        setup_registration(hass, self._store, data)

        return self.json({
            CONF_CLOUDHOOK_URL: data.get(CONF_CLOUDHOOK_URL),
            CONF_SECRET: data.get(CONF_SECRET),
            CONF_WEBHOOK_ID: data[CONF_WEBHOOK_ID],
        }, status_code=HTTP_CREATED)