"""The tests for the logbook component."""
# pylint: disable=protected-access
from datetime import timedelta
import unittest
from unittest.mock import patch

from homeassistant.components import sun
import homeassistant.core as ha
from homeassistant.const import (
    EVENT_STATE_CHANGED, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
    ATTR_HIDDEN, STATE_NOT_HOME, STATE_ON, STATE_OFF)
import homeassistant.util.dt as dt_util
from homeassistant.components import logbook
from homeassistant.bootstrap import setup_component

from tests.common import mock_http_component, get_test_home_assistant


class TestComponentLogbook(unittest.TestCase):
    """Test the History component."""

    EMPTY_CONFIG = logbook.CONFIG_SCHEMA({logbook.DOMAIN: {}})

    def setUp(self):
        """Setup things to be run when tests are started."""
        self.hass = get_test_home_assistant()
        mock_http_component(self.hass)
        self.hass.config.components += ['frontend', 'recorder', 'api']
        with patch('homeassistant.components.logbook.'
                   'register_built_in_panel'):
            assert setup_component(self.hass, logbook.DOMAIN,
                                   self.EMPTY_CONFIG)

    def tearDow
n(self):
        """Stop everything that was started."""
        self.hass.stop()

    def test_service_call_create_logbook_entry(self):
        """Test if service call create log book entry."""
        calls = []

        @ha.callback
        def event_listener(event):
            calls.append(event)

        self.hass.bus.listen(logbook.EVENT_LOGBOOK_ENTRY, event_listener)
        self.hass.service
s.call(logbook.DOMAIN, 'log', {
            logbook.ATTR_NAME: 'Alarm',
            logbook.ATTR_MESSAGE: 'is triggered',
            logbook.ATTR_DOMAIN: 'switch',
            logbook.ATTR_ENTITY_ID: 'switch.test_switch'
        }, True)

        # Logbook entry service call results in firing an event.
        # Our service call will unblock when the event listeners have been
        # scheduled. This means that they may not have been processed yet.
        self.hass.block_till_done()

        self.assertEqual(1, len(calls))
        last_call = calls[-1]

        self.assertEqual('Alarm', last_call.data.get(logbook.ATTR_NAME))
        self.assertEqual('is triggered', last_call.data.get(
            logbook.ATTR_MESSAGE))
        self.assertEqual('switch', last_call.data.get(logbook.ATTR_DOMAIN))
        self.assertEqual('switch.test_switch', last_call.data.get(
            logbook.ATTR_ENTITY_ID))

    def test_service_call_create_log_book_entry_no_message(self):
        """Test if service call create log book entry without message."""
        calls = []

        @ha.callback
        def event_listener(event):
            calls.append(event)

        self.hass.bus.listen(logbook.EVENT_LOGBOOK_ENTRY, event_listener)
        self.hass.services.call(logbook.DOMAIN, 'log', {}, True)

        # Logbook entry service call results in firing an event.
        # Our service call will unblock when the event listeners have been
        # scheduled. This means that they may not have been processed yet.
        self.hass.block_till_done()

        self.assertEqual(0, len(calls))

    def test_humanify_filter_sensor(self):
        """Test humanify filter too frequent sensor values."""
        entity_id = 'sensor.bla'

        pointA = dt_util.utcnow().replace(minute=2)
        pointB = pointA.replace(minute=5)
        pointC = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES)

        eventA = self.create_state_changed_event(pointA, entity_id, 10)
        eventB = self.create_state_changed_event(pointB, entity_id, 20)
        eventC = self.create_state_changed_event(pointC, entity_id, 30)

        entries = list(logbook.humanify((eventA, eventB, eventC)))

        self.assertEqual(2, len(entries))
        self.assert_entry(
            entries[0], pointB, 'bla', domain='sensor', entity_id=entity_id)

        self.assert_entry(
            entries[1], pointC, 'bla', domain='sensor', entity_id=entity_id)

    def test_filter_continuous_sensor_values(self):
        """Test remove continuous sensor events from logbook."""
        entity_id = 'sensor.bla'
        pointA = dt_util.utcnow()
        attributes = {'unit_of_measurement': 'foo'}
        eventA = self.create_state_changed_event(
            pointA, entity_id, 10, attributes)

        entries = list(logbook.humanify((eventA,)))

        self.assertEqual(0, len(entries))

    def test_exclude_events_hidden(self):
        """Test if events are excluded if entity is hidden."""
        entity_id = 'sensor.bla'
        entity_id2 = 'sensor.blu'
        pointA = dt_util.utcnow()
        pointB = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES)

        eventA = self.create_state_changed_event(pointA, entity_id, 10,
                                                 {ATTR_HIDDEN: 'true'})
        eventB = self.create_state_changed_event(pointB, entity_id2, 20)

        events = logbook._exclude_events((ha.Event(EVENT_HOMEASSISTANT_STOP),
                                          eventA, eventB), self.EMPTY_CONFIG)
        entries = list(logbook.humanify(events))

        self.assertEqual(2, len(entries))
        self.assert_entry(
            entries[0], name='Home Assistant', message='stopped',
            domain=ha.DOMAIN)
        self.assert_entry(
            entries[1], pointB, 'blu', domain='sensor', entity_id=entity_id2)

    def test_exclude_events_entity(self):
        """Test if events are filtered if entity is excluded in config."""
        entity_id = 'sensor.bla'
        entity_id2 = 'sensor.blu'
        pointA = dt_util.utcnow()
        pointB = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES)

        eventA = self.create_state_changed_event(pointA, entity_id, 10)
        eventB = self.create_state_changed_event(pointB, entity_id2, 20)

        config = logbook.CONFIG_SCHEMA({
            ha.DOMAIN: {},
            logbook.DOMAIN: {logbook.CONF_EXCLUDE: {
                logbook.CONF_ENTITIES: [entity_id, ]}}})
        events = logbook._exclude_events((ha.Event(EVENT_HOMEASSISTANT_STOP),
                                          eventA, eventB), config)
        entries = list(logbook.humanify(events))

        self.assertEqual(2, len(entries))
        self.assert_entry(
            entries[0], name='Home Assistant', message='stopped',
            domain=ha.DOMAIN)
        self.assert_entry(
            entries[1], pointB, 'blu', domain='sensor', entity_id=entity_id2)

    def test_exclude_events_domain(self):
        """Test if events are filtered if domain is excluded in config."""
        entity_id = 'switch.bla'
        entity_id2 = 'sensor.blu'
        pointA = dt_util.utcnow()
        pointB = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES)

        eventA = self.create_state_changed_event(pointA, entity_id, 10)
        eventB = self.create_state_changed_event(pointB, entity_id2, 20)

        config = logbook.CONFIG_SCHEMA({
            ha.DOMAIN: {},
            logbook.DOMAIN: {logbook.CONF_EXCLUDE: {
                logbook.CONF_DOMAINS: ['switch', ]}}})
        events = logbook._exclude_events((ha.Event(EVENT_HOMEASSISTANT_START),
                                          eventA, eventB), config)
        entries = list(logbook.humanify(events))

        self.assertEqual(2, len(entries))
        self.assert_entry(entries[0], name='Home Assistant', message='started',
                          domain=ha.DOMAIN)
        self.assert_entry(entries[1], pointB, 'blu', domain='sensor',
                          entity_id=entity_id2)

    def test_exclude_automation_events(self):
        """Test if automation entries can be excluded by entity_id."""
        name = 'My Automation Rule'
        message = 'has been triggered'
        domain = 'automation'
        entity_id = 'automation.my_automation_rule'
        entity_id2 = 'automation.my_automation_rule_2'
        entity_id2 = 'sensor.blu'

        eventA = ha.Event(logbook.EVENT_LOGBOOK_ENTRY, {
                logbook.ATTR_NAME: name,
                logbook.ATTR_MESSAGE: message,
                logbook.ATTR_