"""Component to configure Home Assistant via an API."""
import asyncio
import os

import voluptuous as vol

from homeassistant.core import callback
from homeassistant.const import EVENT_COMPONENT_LOADED
from homeassistant.bootstrap import (
    async_prepare_setup_platform, ATTR_COMPONENT)
from homeassistant.components.frontend import register_built_in_panel
from homeassistant.components.http import HomeAssistantView
from homeassistant.util.yaml import load_yaml, dump

DOMAIN = 'config'
DEPENDENCIES = ['http']
SECTIONS = ('core', 'group', 'hassbian')
ON_DEMAND = ('zwave', )


@asyncio.coroutine
def async_setup(hass, config):
    """Setup the config component."""
    register_built_in_panel(hass, 'config', 'Configuration', 'mdi:settings')

    @asyncio.coroutine
    def setup_panel(panel_name):
        """Setup a panel."""
        panel = yield from async_prepare_setup_platform(hass, config, DOMAIN,
                                                        panel_name)

        if not panel:
            return

        success = yield from panel.async_setup(hass)

        if success:
            key = '{}.{}'.format(DOMAIN, panel_name)
            hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: key})
            hass.config.components.add(key)

    tasks = [setup_panel(panel_name) for panel_name in SECTIONS]

    for panel_name in ON_DEMAND:
        if panel_name in hass.config.components:
            tasks.append(setup_panel(panel_name))

    if tasks:
        yield from asyncio.wait(tasks, loop=hass.loop)

    @callback
    def component_loaded(event):
        """Respond to components being loaded."""
        panel_name = event.data.get(ATTR_COMPONENT)
        if panel_name in ON_DEMAND:
            hass.async_add_job(setup_panel(panel_name))

    hass.bus.async_listen(EVENT_COMPONENT_LOADED, component_loaded)

    return True


class EditKeyBasedConfigView(HomeAssistantView):
    """Configure a Group endpoint."""

    def __init__(self, component, config_type, path, key_schema, data_schema,
                 *, post_write_hook=None):
        """Initialize a config view."""
        self.url = '/api/config/%s/%s/{config_key}' % (component, config_type)
        self.name = 'api:config:%s:%s' % (component, config_type)
        self.path = path
        self.key_schema = key_schema
        self.data_schema = data_schema
        self.post_write_hook = post_write_hook

    @asyncio.coroutine
    def get(self, request, config_key):
        """Fetch device specific config."""
        hass = request.app['hass']
        current = yield from hass.loop.run_in_executor(
            None, _read, hass.config.path(self.path))
        return self.json(current.get(config_key, {}))

    @asyncio.coroutine
    def post(self, request, config_key):
        """Validate config and return results."""
        try:
            data = yield from request.json()
        except ValueError:
            return self.json_message('Invalid JSON specified', 400)

        try:
            self.key_schema(config_key)
        except vol.Invalid as err:
            return self.json_message('Key malformed: {}'.format(err), 400)

        try:
            # We just validate, we don't store that data because
            # we don't want to store the defaults.
            self.data_schema(data)
        except vol.Invalid as err:
            return self.json_message('Message malformed: {}'.format(err), 400)

        hass = request.app['hass']
        path = hass.config.path(self.path)

        current = yield from hass.loop.run_in_executor(None, _read, path)
        current.setdefault(config_key, {}).update(data)

        yield from hass.loop.run_in_executor(None, _write, path, current)

        if self.post_write_hook is not None:
            hass.async_add_job(self.post_write_hook(hass))

        return self.json({
            'result': 'ok',
        })


def _read(path):
    """Read YAML helper."""
    if not os.path.isfile(path):
        with open(path, 'w'):
            pass
        return {}

    return load_yaml(path)


def _write(path, data):
    """Write YAML helper."""
    # Do it before opening file. If dump causes error it will now not
    # truncate the file.
    data = dump(data)
    with open(path, 'w', encoding='utf-8') as outfile:
        outfile.write(data)
