"""
Support for Google Actions Smart Home Control.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/google_assistant/
"""
import asyncio
import logging

from aiohttp.hdrs import AUTHORIZATION
from aiohttp.web import Request, Response  # NOQA

from homeassistant.const import HTTP_UNAUTHORIZED

# Typing imports
# pylint: disable=using-constant-test,unused-import,ungrouped-imports
from homeassistant.components.http import HomeAssistantView
from homeassistant.core import HomeAssistant, callback  # NOQA
from homeassistant.helpers.entity import Entity  # NOQA

from .const import (
    GOOGLE_ASSISTANT_API_ENDPOINT,
    CONF_ACCESS_TOKEN,
    CONF_EXPOSE_BY_DEFAULT,
    CONF_EXPOSED_DOMAINS,
    CONF_AGENT_USER_ID,
    CONF_ENTITY_CONFIG,
    CONF_EXPOSE,
    )
from .smart_home import async_handle_message, Config

_LOGGER = logging.getLogger(__name__)


@callback
def async_register_http(hass, cfg):
    """Register HTTP views for Google Assistant."""
    access_token = cfg.get(CONF_ACCESS_TOKEN)
    expose_by_default = cfg.get(CONF_EXPOSE_BY_DEFAULT)
    exposed_domains = cfg.get(CONF_EXPOSED_DOMAINS)
    agent_user_id = cfg.get(CONF_AGENT_USER_ID)
    entity_config = cfg.get(CONF_ENTITY_CONFIG) or {}

    def is_exposed(entity) -> bool:
        """Determine if an entity should be exposed to Google Assistant."""
        if entity.attributes.get('view') is not None:
            # Ignore entities that are views
            return False

        explicit_expose = \
            entity_config.get(entity.entity_id, {}).get(CONF_EXPOSE)

        domain_exposed_by_default = \
            expose_by_default and entity.domain in exposed_domains

        # Expose an entity if the entity's domain is exposed by default and
        # the configuration doesn't explicitly exclude it from being
        # exposed, or if the entity is explicitly exposed
        is_default_exposed = \
            domain_exposed_by_default and explicit_expose is not False

        return is_default_exposed or explicit_expose

    gass_config = Config(is_exposed, agent_user_id, entity_config)
    hass.http.register_view(
        GoogleAssistantView(access_token, gass_config))


class GoogleAssistantView(HomeAssistantView):
    """Handle Google Assistant requests."""

    url = GOOGLE_ASSISTANT_API_ENDPOINT
    name = 'api:google_assistant'
    requires_auth = False  # Uses access token from oauth flow

    def __init__(self, access_token, gass_config):
        """Initialize the Google Assistant request handler."""
        self.access_token = access_token
        self.gass_config = gass_config

    @asyncio.coroutine
    def post(self, request: Request) -> Response:
        """Handle Google Assistant requests."""
        auth = request.headers.get(AUTHORIZATION, None)
        if 'Bearer {}'.format(self.access_token) != auth:
            return self.json_message(
                "missing authorization", status_code=HTTP_UNAUTHORIZED)

        message = yield from request.json()  # type: dict
        result = yield from async_handle_message(
            request.app['hass'], self.gass_config, message)
        return self.json(result)
