"""The tests for the emulated Hue component."""
import asyncio
import json
from ipaddress import ip_address
from unittest.mock import patch

from aiohttp.hdrs import CONTENT_TYPE
import pytest
from tests.common import get_test_instance_port

from homeassistant import const, setup
from homeassistant.components import (
    fan, http, light, script, emulated_hue, media_player, cover, climate)
from homeassistant.components.emulated_hue import Config
from homeassistant.components.emulated_hue.hue_api import (
    HUE_API_STATE_ON, HUE_API_STATE_BRI, HUE_API_STATE_HUE, HUE_API_STATE_SAT,
    HueUsernameView, HueOneLightStateView,
    HueAllLightsStateView, HueOneLightChangeView, HueAllGroupsStateView)
from homeassistant.const import STATE_ON, STATE_OFF

import homeassistant.util.dt as dt_util
from datetime import timedelta
from tests.common import async_fire_time_changed

HTTP_SERVER_PORT = get_test_instance_port()
BRIDGE_SERVER_PORT = get_test_instance_port()

BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}'
JSON_HEADERS = {CONTENT_TYPE: const.CONTENT_TYPE_JSON}


@pytest.fixture
def hass_hue(loop, hass):
    """Set up a Home Assistant instance for these tests."""
    # We need to do this to get access to homeassistant/turn_(on,off)
    loop.run_until_complete(setup.async_setup_component(
        hass, 'homeassistant', {}))

    loop.run_until_complete(setup.async_setup_component(
        hass, http.DOMAIN,
        {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}))

    with patch('homeassistant.components'
               '.emulated_hue.UPNPResponderThread'):
        loop.run_until_complete(
            setup.async_setup_component(hass, emulated_hue.DOMAIN, {
                emulated_hue.DOMAIN: {
                    emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT,
                    emulated_hue.CONF_EXPOSE_BY_DEFAULT: True
                }
            }))

    loop.run_until_complete(
        setup.async_setup_component(hass, light.DOMAIN, {
            'light': [
                {
                    'platform': 'demo',
                }
            ]
        }))

    loop.run_until_complete(
        setup.async_setup_component(hass, script.DOMAIN, {
            'script': {
                'set_kitchen_light': {
                    'sequence': [
                        {
                            'service_template':
                                "light.turn_{{ requested_state }}",
                            'data_template': {
                                'entity_id': 'light.kitchen_lights',
                                'brightness': "{{ requested_level }}"
                                }
                        }
                    ]
                }
            }
        }))

    loop.run_until_complete(
        setup.async_setup_component(hass, climate.DOMAIN, {
            'climate': [
                {
                    'platform': 'demo',
                }
            ]
        }))

    loop.run_until_complete(
        setup.async_setup_component(hass, media_player.DOMAIN, {
            'media_player': [
                {
                    'platform': 'demo',
                }
            ]
        }))

    loop.run_until_complete(
        setup.async_setup_component(hass, fan.DOMAIN, {
            'fan': [
                {
                    'platform': 'demo',
                }
            ]
        }))

    loop.run_until_complete(
        setup.async_setup_component(hass, cover.DOMAIN, {
            'cover': [
                {
                    'platform': 'demo',
                }
            ]
        }))

    # Kitchen light is explicitly excluded from being exposed
    kitchen_light_entity = hass.states.get('light.kitchen_lights')
    attrs = dict(kitchen_light_entity.attributes)
    attrs[emulated_hue.ATTR_EMULATED_HUE] = False
    hass.states.async_set(
        kitchen_light_entity.entity_id, kitchen_light_entity.state,
        attributes=attrs)

    # Ceiling Fan is explicitly excluded from being exposed
    ceiling_fan_entity = hass.states.get('fan.ceiling_fan')
    attrs = dict(ceiling_fan_entity.attributes)
    attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = True
    hass.states.async_set(
        ceiling_fan_entity.entity_id, ceiling_fan_entity.state,
        attributes=attrs)

    # Expose the script
    script_entity = hass.states.get('script.set_kitchen_light')
    attrs = dict(script_entity.attributes)
    attrs[emulated_hue.ATTR_EMULATED_HUE] = True
    hass.states.async_set(
        script_entity.entity_id, script_entity.state, attributes=attrs
    )

    # Expose cover
    cover_entity = hass.states.get('cover.living_room_window')
    attrs = dict(cover_entity.attributes)
    attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = False
    hass.states.async_set(
        cover_entity.entity_id, cover_entity.state, attributes=attrs
    )

    # Expose Hvac
    hvac_entity = hass.states.get('climate.hvac')
    attrs = dict(hvac_entity.attributes)
    attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = False
    hass.states.async_set(
        hvac_entity.entity_id, hvac_entity.state, attributes=attrs
    )

    # Expose HeatPump
    hp_entity = hass.states.get('climate.heatpump')
    attrs = dict(hp_entity.attributes)
    attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = False
    hass.states.async_set(
        hp_entity.entity_id, hp_entity.state, attributes=attrs
    )

    return hass


@pytest.fixture
def hue_client(loop, hass_hue, aiohttp_client):
    """Create web client for emulated hue api."""
    web_app = hass_hue.http.app
    config = Config(None, {
        emulated_hue.CONF_TYPE: emulated_hue.TYPE_ALEXA,
        emulated_hue.CONF_ENTITIES: {
            'light.bed_light': {
                emulated_hue.CONF_ENTITY_HIDDEN: True
            },
            'cover.living_room_window': {
                emulated_hue.CONF_ENTITY_HIDDEN: False
            }

        }
    })

    HueUsernameView().register(web_app, web_app.router)
    HueAllLightsStateView(config).register(web_app, web_app.router)
    HueOneLightStateView(config).register(web_app, web_app.router)
    HueOneLightChangeView(config).register(web_app, web_app.router)
    HueAllGroupsStateView(config).register(web_app, web_app.router)

    return loop.run_until_complete(aiohttp_client(web_app))


@asyncio.coroutine
def test_discover_lights(hue_client):
    """Test the discovery of lights."""
    result = yield from hue_client.get('/api/username/lights')

    assert result.status == 200
    assert 'application/json' in result.headers['content-type']

    result_json = yield from result.json()

    devices = set(val['uniqueid'] for val in result_json.values())

    # Make sure the lights we added to the config are there
    assert 'light.ceiling_lights' in devices
    assert 'light.bed_light' not in devices
    assert 'script.set_kitchen_light' in devices
    assert 'light.kitchen_lights' not in devices
    assert 'media_player.living_room' in devices
    assert 'media_player.bedroom' in devices
    assert 'media_player.walkman' in devices
    assert 'media_player.lounge_room' in devices
    assert 'fan.living_room_fan' in devices
    assert 'fan.ceiling_fan' not in devices
    assert 'cover.living_room_window' in devices
    assert 'climate.hvac' in devices
    assert 'climate.heatpump' in devices
    assert 'climate.ecobee' not in devices


@asyncio.coroutine
def test_get_light_state(hass_hue, hue_client):
    """Test the getting of light state."""
    # Turn office light on and set to 127 brightness, and set light color
    yield from hass_hue.services.async_call(
        light.DOMAIN, const.SERVICE_TURN_ON,
        {
            const.ATTR_ENTITY_ID: 'light.ceiling_lights',
            light.ATTR_BRIGHTNESS: 127,
            light.ATTR_RGB_COLOR: (1, 2, 7)
        },
        blocking=True)

    office_json = yield from perform_get_light_state(
        hue_client, 'light.ceiling_lights', 200)

    assert office_json['state'][HUE_API_STATE_ON] is True
    assert office_json['state'][HUE_API_STATE_BRI] == 127
    assert office_json['state'][HUE_API_STATE_HUE] == 41869
    assert office_json['state'][HUE_API_STATE_SAT] == 217

    # Check all lights view
    result = yield from hue_client.get('/api/username/lights')

    assert result.status == 200
    assert 'application/json' in result.headers['content-type']

    result_json = yield from result.json()

    assert 'light.ceiling_lights' in result_json
    assert result_json['light.ceiling_lights']['state'][HUE_API_STATE_BRI] == \
        127

    # Turn office light off
    yield from hass_hue.services.async_call(
        light.DOMAIN, const.SERVICE_TURN_OFF,
        {
            const.ATTR_ENTITY_ID: 'light.ceiling_lights'
        },
        blocking=True)

    office_json = yield from perform_get_light_state(
        hue_client, 'light.ceiling_lights', 200)

    assert office_json['state'][HUE_API_STATE_ON] is False
    assert office_json['state'][HUE_API_STATE_BRI] == 0
    assert office_json['state'][HUE_API_STATE_HUE] == 0
    assert office_json['state'][HUE_API_STATE_SAT] == 0

    # Make sure bedroom light isn't accessible
    yield from perform_get_light_state(
        hue_client, 'light.bed_light', 404)

    # Make sure kitchen light isn't accessible
    yield from perform_get_light_state(
        hue_client, 'light.kitchen_lights', 404)


@asyncio.coroutine
def test_put_light_state(hass_hue, hue_client):
    """Test the setting of light states."""
    yield from perform_put_test_on_ceiling_lights(hass_hue, hue_client)

    # Turn the bedroom light on first
    yield from hass_hue.services.async_call(
        light.DOMAIN, const.SERVICE_TURN_ON,
        {const.ATTR_ENTITY_ID: 'light.ceiling_lights',
         light.ATTR_BRIGHTNESS: 153},
        blocking=True)

    ceiling_lights = hass_hue.states.get('light.ceiling_lights')
    assert ceiling_lights.state == STATE_ON
    assert ceiling_lights.attributes[light.ATTR_BRIGHTNESS] == 153

    # update light state through api
    yield from perform_put_light_state(
        hass_hue, hue_client,
        'light.ceiling_lights', True,
        hue=4369, saturation=127, brightness=123)

    # go through api to get the state back
    ceiling_json = yield from perform_get_light_state(
        hue_client, 'light.ceiling_lights', 200)
    assert ceiling_json['state'][HUE_API_STATE_BRI] == 123
    assert ceiling_json['state'][HUE_API_STATE_HUE] == 4369
    assert ceiling_json['state'][HUE_API_STATE_SAT] == 127

    # Go through the API to turn it off
    ceiling_result = yield from perform_put_light_state(
        hass_hue, hue_client,
        'light.ceiling_lights', False)

    ceiling_result_json = yield from ceiling_result.json()

    assert ceiling_result.status == 200
    assert 'application/json' in ceiling_result.headers['content-type']

    assert len(ceiling_result_json) == 1

    # Check to make sure the state changed
    ceiling_lights = hass_hue.states.get('light.ceiling_lights')
    assert ceiling_lights.state == STATE_OFF
    ceiling_json = yield from perform_get_light_state(
        hue_client, 'light.ceiling_lights', 200)
    assert ceiling_json['state'][HUE_API_STATE_BRI] == 0
    assert ceiling_json['state'][HUE_API_STATE_HUE] == 0
    assert ceiling_json['state'][HUE_API_STATE_SAT] == 0

    # Make sure we can't change the bedroom light state
    bedroom_result = yield from perform_put_light_state(
        hass_hue, hue_client,
        'light.bed_light', True)
    assert bedroom_result.status == 404

    # Make sure we can't change the kitchen light state
    kitchen_result = yield from perform_put_light_state(
        hass_hue, hue_client,
        'light.kitchen_light', True)
    assert kitchen_result.status == 404


@asyncio.coroutine
def test_put_light_state_script(hass_hue, hue_client):
    """Test the setting of script variables."""
    # Turn the kitchen light off first
    yield from hass_hue.services.async_call(
        light.DOMAIN, const.SERVICE_TURN_OFF,
        {const.ATTR_ENTITY_ID: 'light.kitchen_lights'},
        blocking=True)

    # Emulated hue converts 0-100% to 0-255.
    level = 23
    brightness = round(level * 255 / 100)

    script_result = yield from perform_put_light_state(
        hass_hue, hue_client,
        'script.set_kitchen_light', True, brightness)

    script_result_json = yield from script_result.json()

    assert script_result.status == 200