"""The tests for the Template Binary sensor platform."""
from datetime import timedelta
import logging
from unittest.mock import patch

from homeassistant import setup
from homeassistant.components import binary_sensor
from homeassistant.const import (
    ATTR_DEVICE_CLASS,
    EVENT_HOMEASSISTANT_START,
    STATE_OFF,
    STATE_ON,
    STATE_UNAVAILABLE,
)
from homeassistant.core import CoreState
import homeassistant.util.dt as dt_util

from tests.common import assert_setup_component, async_fire_time_changed


async def test_setup(hass):
    """Test the setup."""
    config = {
        "binary_sensor": {
            "platform": "template",
            "sensors": {
                "test": {
                    "friendly_name": "virtual thingy",
                    "value_template": "{{ foo }}",
                    "device_class": "motion",
                }
            },
        }
    }
    with assert_setup_component(1):
        assert await setup.async_setup_component(hass, binary_sensor.DOMAIN, config)


async def test_setup_no_sensors(hass):
    """Test setup with no sensors."""
    with assert_setup_component(0):
        assert await setup.async_setup_component(
            hass, binary_sensor.DOMAIN, {"binary_sensor": {"platform": "template"}}
        )


async def test_setup_invalid_device(hass):
    """Test the setup with invalid devices."""
    with assert_setup_component(0):
        assert await setup.async_setup_component(
            hass,
            binary_sensor.DOMAIN,
            {"binary_sensor": {"platform": "template", "sensors": {"foo bar": {}}}},
        )


async def test_setup_invalid_device_class(hass):
    """Test setup with invalid sensor class."""
    with assert_setup_component(0):
        assert await setup.async_setup_component(
            hass,
            binary_sensor.DOMAIN,
            {
                "binary_sensor": {
                    "platform": "template",
                    "sensors": {
                        "test": {
                            "value_template": "{{ foo }}",
                            "device_class": "foobarnotreal",
                        }
                    },
                }
            },
        )


async def test_setup_invalid_missing_template(hass):
    """Test setup with invalid and missing template."""
    with assert_setup_component(0):
        assert await setup.async_setup_component(
            hass,
            binary_sensor.DOMAIN,
            {
                "binary_sensor": {
                    "platform": "template",
                    "sensors": {"test": {"device_class": "motion"}},
                }
            },
        )


async def test_icon_template(hass):
    """Test icon template."""
    with assert_setup_component(1):
        assert await setup.async_setup_component(
            hass,
            binary_sensor.DOMAIN,
            {
                "binary_sensor": {
                    "platform": "template",
                    "sensors": {
                        "test_template_sensor": {
                            "value_template": "{{ states.sensor.xyz.state }}",
                            "icon_template": "{% if "
                            "states.binary_sensor.test_state.state == "
                            "'Works' %}"
                            "mdi:check"
                            "{% endif %}",
                        }
                    },
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test_template_sensor")
    assert state.attributes.get("icon") == ""

    hass.states.async_set("binary_sensor.test_state", "Works")
    await hass.async_block_till_done()
    state = hass.states.get("binary_sensor.test_template_sensor")
    assert state.attributes["icon"] == "mdi:check"


async def test_entity_picture_template(hass):
    """Test entity_picture template."""
    with assert_setup_component(1):
        assert await setup.async_setup_component(
            hass,
            binary_sensor.DOMAIN,
            {
                "binary_sensor": {
                    "platform": "template",
                    "sensors": {
                        "test_template_sensor": {
                            "value_template": "{{ states.sensor.xyz.state }}",
                            "entity_picture_template": "{% if "
                            "states.binary_sensor.test_state.state == "
                            "'Works' %}"
                            "/local/sensor.png"
                            "{% endif %}",
                        }
                    },
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test_template_sensor")
    assert state.attributes.get("entity_picture") == ""

    hass.states.async_set("binary_sensor.test_state", "Works")
    await hass.async_block_till_done()
    state = hass.states.get("binary_sensor.test_template_sensor")
    assert state.attributes["entity_picture"] == "/local/sensor.png"


async def test_attribute_templates(hass):
    """Test attribute_templates template."""
    with assert_setup_component(1):
        assert await setup.async_setup_component(
            hass,
            binary_sensor.DOMAIN,
            {
                "binary_sensor": {
                    "platform": "template",
                    "sensors": {
                        "test_template_sensor": {
                            "value_template": "{{ states.sensor.xyz.state }}",
                            "attribute_templates": {
                                "test_attribute": "It {{ states.sensor.test_state.state }}."
                            },
                        }
                    },
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test_template_sensor")
    assert state.attributes.get("test_attribute") == "It ."
    hass.states.async_set("sensor.test_state", "Works2")
    await hass.async_block_till_done()
    hass.states.async_set("sensor.test_state", "Works")
    await hass.async_block_till_done()
    state = hass.states.get("binary_sensor.test_template_sensor")
    assert state.attributes["test_attribute"] == "It Works."


async def test_match_all(hass):
    """Test template that is rerendered on any state lifecycle."""
    with patch(
        "homeassistant.components.template.binary_sensor."
        "BinarySensorTemplate._update_state"
    ) as _update_state:
        with assert_setup_component(1):
            assert await setup.async_setup_component(
                hass,
                binary_sensor.DOMAIN,
                {
                    "binary_sensor": {
                        "platform": "template",
                        "sensors": {
                            "match_all_template_sensor": {
                                "value_template": (
                                    "{% for state in states %}"
                                    "{% if state.entity_id == 'sensor.humidity' %}"
                                    "{{ state.entity_id }}={{ state.state }}"
                                    "{% endif %}"
                                    "{% endfor %}"
                                ),
                            },
                        },
                    }
                },
            )

            await hass.async_start()
            await hass.async_block_till_done()
            init_calls = len(_update_state.mock_calls)

            hass.states.async_set("sensor.any_state", "update")
            await hass.async_block_till_done()
            assert len(_update_state.mock_calls) == init_calls


async def test_event(hass):
    """Test the event."""
    config = {
        "binary_sensor": {
            "platform": "template",
            "sensors": {
                "test": {
                    "friendly_name": "virtual thingy",
                    "value_template": "{{ states.sensor.test_state.state == 'on' }}",
                    "device_class": "motion",
                }
            },
        }
    }
    with assert_setup_component(1):
        assert await setup.async_setup_component(hass, binary_sensor.DOMAIN, config)

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "off"

    hass.states.async_set("sensor.test_state", "on")
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "on"


async def test_template_delay_on(hass):
    """Test binary sensor template delay on."""
    config = {
        "binary_sensor": {
            "platform": "template",
            "sensors": {
                "test": {
                    "friendly_name": "virtual thingy",
                    "value_template": "{{ states.sensor.test_state.state == 'on' }}",
                    "device_class": "motion",
                    "delay_on": 5,
                }
            },
        }
    }
    await setup.async_setup_component(hass, binary_sensor.DOMAIN, config)
    await hass.async_block_till_done()
    await hass.async_start()

    hass.states.async_set("sensor.test_state", "on")
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "off"

    future = dt_util.utcnow() + timedelta(seconds=5)
    async_fire_time_changed(hass, future)
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "on"

    # check with time changes
    hass.states.async_set("sensor.test_state", "off")
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "off"

    hass.states.async_set("sensor.test_state", "on")
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "off"

    hass.states.async_set("sensor.test_state", "off")
    await hass.async_block_till_done()

    state = hass.states.get("binary_sensor.test")
    assert state.state == "off"

    future = dt_util.utcnow() + timedelta(seconds=5)
    async_fire_time_changed(hass, future)
    await hass.async_block_till_done()