"""This module provides WSGI application to serve the Home Assistant API."""
import hmac
import json
import logging
import mimetypes
import threading
import re
import voluptuous as vol

import homeassistant.core as ha
import homeassistant.remote as rem
from homeassistant import util
from homeassistant.const import (
    SERVER_PORT, HTTP_HEADER_HA_AUTH, HTTP_HEADER_CACHE_CONTROL,
    HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
    HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, ALLOWED_CORS_HEADERS)
from homeassistant.helpers.entity import split_entity_id
import homeassistant.util.dt as dt_util
import homeassistant.helpers.config_validation as cv

DOMAIN = "http"
REQUIREMENTS = ("eventlet==0.19.0", "static3==0.7.0", "Werkzeug==0.11.5",)

CONF_API_PASSWORD = "api_password"
CONF_SERVER_HOST = "server_host"
CONF_SERVER_PORT = "server_port"
CONF_DEVELOPMENT = "development"
CONF_SSL_CERTIFICATE = 'ssl_certificate'
CONF_SSL_KEY = 'ssl_key'
CONF_CORS_ORIGINS = 'cors_allowed_origins'

DATA_API_PASSWORD = 'api_password'

_FINGERPRINT = re.compile(r'^(.+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)

_LOGGER = logging.getLogger(__name__)

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Optional(CONF_API_PASSWORD): cv.string,
        vol.Optional(CONF_SERVER_HOST): cv.string,
        vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT):
            vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)),
        vol.Optional(CONF_DEVELOPMENT): cv.string,
        vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
        vol.Optional(CONF_SSL_KEY): cv.isfile,
        vol.Optional(CONF_CORS_ORIGINS): cv.ensure_list
    }),
}, extra=vol.ALLOW_EXTRA)


class HideSensitiveFilter(logging.Filter):
    """Filter API password calls."""

    # pylint: disable=too-few-public-methods
    def __init__(self, hass):
        """Initialize sensitive data filter."""
        super().__init__()
        self.hass = hass

    def filter(self, record):
        """Hide sensitive data in messages."""
        if self.hass.wsgi.api_password is None:
            return True

        record.msg = record.msg.replace(self.hass.wsgi.api_password, '*******')

        return True


def setup(hass, config):
    """Set up the HTTP API and debug interface."""
    _LOGGER.addFilter(HideSensitiveFilter(hass))

    conf = config.get(DOMAIN, {})

    api_password = util.convert(conf.get(CONF_API_PASSWORD), str)
    server_host = conf.get(CONF_SERVER_HOST, '0.0.0.0')
    server_port = conf.get(CONF_SERVER_PORT, SERVER_PORT)
    development = str(conf.get(CONF_DEVELOPMENT, "")) == "1"
    ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
    ssl_key = conf.get(CONF_SSL_KEY)
    cors_origins = conf.get(CONF_CORS_ORIGINS, [])

    server = HomeAssistantWSGI(
        hass,
        development=development,
        server_host=server_host,
        server_port=server_port,
        api_password=api_password,
        ssl_certificate=ssl_certificate,
        ssl_key=ssl_key,
        cors_origins=cors_origins
    )

    hass.bus.listen_once(
        ha.EVENT_HOMEASSISTANT_START,
        lambda event:
        threading.Thread(target=server.start, daemon=True,
                         name='WSGI-server').start())

    hass.wsgi = server
    hass.config.api = rem.API(server_host if server_host != '0.0.0.0'
                              else util.get_local_ip(),
                              api_password, server_port,
                              ssl_certificate is not None)

    return True


def request_class():
    """Generate request class.

    Done in method because of imports.
    """
    from werkzeug.exceptions import BadRequest
    from werkzeug.wrappers import BaseRequest, AcceptMixin
    from werkzeug.utils import cached_property

    class Request(BaseRequest, AcceptMixin):
        """Base class for incoming requests."""

        @cached_property
        def json(self):
            """Get the result of json.loads if possible."""
            if not self.data:
                return None
            # elif 'json' not in self.environ.get('CONTENT_TYPE', ''):
            #     raise BadRequest('Not a JSON request')
            try:
                return json.loads(self.data.decode(
                    self.charset, self.encoding_errors))
            except (TypeError, ValueError):
                raise BadRequest('Unable to read JSON request')

    return Request


def routing_map(hass):
    """Generate empty routing map with HA validators."""
    from werkzeug.routing import Map, BaseConverter, ValidationError

    class EntityValidator(BaseConverter):
        """Validate entity_id in urls."""

        regex = r"(\w+)\.(\w+)"

        def __init__(self, url_map, exist=True, domain=None):
            """Initilalize entity validator."""
            super().__init__(url_map)
            self._exist = exist
            self._domain = domain

        def to_python(self, value):
            """Validate entity id."""
            if self._exist and hass.states.get(value) is None:
                raise ValidationError()
            if self._domain is not None and \
               split_entity_id(value)[0] != self._domain:
                raise ValidationError()

            return value

        def to_url(self, value):
            """Convert entity_id for a url."""
            return value