"""The tests for the Template Weather platform."""
import pytest

from homeassistant.components.weather import (
    ATTR_FORECAST,
    ATTR_WEATHER_APPARENT_TEMPERATURE,
    ATTR_WEATHER_CLOUD_COVERAGE,
    ATTR_WEATHER_DEW_POINT,
    ATTR_WEATHER_HUMIDITY,
    ATTR_WEATHER_OZONE,
    ATTR_WEATHER_PRESSURE,
    ATTR_WEATHER_TEMPERATURE,
    ATTR_WEATHER_VISIBILITY,
    ATTR_WEATHER_WIND_BEARING,
    ATTR_WEATHER_WIND_GUST_SPEED,
    ATTR_WEATHER_WIND_SPEED,
    DOMAIN as WEATHER_DOMAIN,
    SERVICE_GET_FORECAST,
    Forecast,
)
from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.core import HomeAssistant


@pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)])
@pytest.mark.parametrize(
    "config",
    [
        {
            "weather": [
                {"weather": {"platform": "demo"}},
                {
                    "platform": "template",
                    "name": "test",
                    "attribution_template": "{{ states('sensor.attribution') }}",
                    "condition_template": "sunny",
                    "forecast_template": "{{ states.weather.demo.attributes.forecast }}",
                    "temperature_template": "{{ states('sensor.temperature') | float }}",
                    "humidity_template": "{{ states('sensor.humidity') | int }}",
                    "pressure_template": "{{ states('sensor.pressure') }}",
                    "wind_speed_template": "{{ states('sensor.windspeed') }}",
                    "wind_bearing_template": "{{ states('sensor.windbearing') }}",
                    "ozone_template": "{{ states('sensor.ozone') }}",
                    "visibility_template": "{{ states('sensor.visibility') }}",
                    "wind_gust_speed_template": "{{ states('sensor.wind_gust_speed') }}",
                    "cloud_coverage_template": "{{ states('sensor.cloud_coverage') }}",
                    "dew_point_template": "{{ states('sensor.dew_point') }}",
                    "apparent_temperature_template": "{{ states('sensor.apparent_temperature') }}",
                },
            ]
        },
    ],
)
async def test_template_state_text(hass: HomeAssistant, start_ha) -> None:
    """Test the state text of a template."""
    for attr, v_attr, value in [
        (
            "sensor.attribution",
            ATTR_ATTRIBUTION,
            "The custom attribution",
        ),
        ("sensor.temperature", ATTR_WEATHER_TEMPERATURE, 22.3),
        ("sensor.humidity", ATTR_WEATHER_HUMIDITY, 60),
        ("sensor.pressure", ATTR_WEATHER_PRESSURE, 1000),
        ("sensor.windspeed", ATTR_WEATHER_WIND_SPEED, 20),
        ("sensor.windbearing", ATTR_WEATHER_WIND_BEARING, 180),
        ("sensor.ozone", ATTR_WEATHER_OZONE, 25),
        ("sensor.visibility", ATTR_WEATHER_VISIBILITY, 4.6),
        ("sensor.wind_gust_speed", ATTR_WEATHER_WIND_GUST_SPEED, 30),
        ("sensor.cloud_coverage", ATTR_WEATHER_CLOUD_COVERAGE, 75),
        ("sensor.dew_point", ATTR_WEATHER_DEW_POINT, 2.2),
        ("sensor.apparent_temperature", ATTR_WEATHER_APPARENT_TEMPERATURE, 25),
    ]:
        hass.states.async_set(attr, value)
        await hass.async_block_till_done()
        state = hass.states.get("weather.test")
        assert state is not None
        assert state.state == "sunny"
        assert state.attributes.get(v_attr) == value


@pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)])
@pytest.mark.parametrize(
    "config",
    [
        {
            "weather": [
                {
                    "platform": "template",
                    "name": "forecast",
                    "condition_template": "sunny",
                    "forecast_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_daily_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_hourly_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_twice_daily_template": "{{ states.weather.forecast_twice_daily.attributes.forecast }}",
                    "temperature_template": "{{ states('sensor.temperature') | float }}",
                    "humidity_template": "{{ states('sensor.humidity') | int }}",
                },
            ]
        },
    ],
)
async def test_forecasts(hass: HomeAssistant, start_ha) -> None:
    """Test forecast service."""
    for attr, _v_attr, value in [
        ("sensor.temperature", ATTR_WEATHER_TEMPERATURE, 22.3),
        ("sensor.humidity", ATTR_WEATHER_HUMIDITY, 60),
    ]:
        hass.states.async_set(attr, value)
        await hass.async_block_till_done()

    hass.states.async_set(
        "weather.forecast",
        "sunny",
        {
            ATTR_FORECAST: [
                Forecast(
                    condition="cloudy",
                    datetime="2023-02-17T14:00:00+00:00",
                    temperature=14.2,
                )
            ]
        },
    )
    hass.states.async_set(
        "weather.forecast_twice_daily",
        "fog",
        {
            ATTR_FORECAST: [
                Forecast(
                    condition="fog",
                    datetime="2023-02-17T14:00:00+00:00",
                    temperature=14.2,
                    is_daytime=True,
                )
            ]
        },
    )
    await hass.async_block_till_done()
    state = hass.states.get("weather.forecast")
    assert state is not None
    assert state.state == "sunny"
    state2 = hass.states.get("weather.forecast_twice_daily")
    assert state2 is not None
    assert state2.state == "fog"

    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "daily"},
        blocking=True,
        return_response=True,
    )
    assert response == {
        "forecast": [
            {
                "condition": "cloudy",
                "datetime": "2023-02-17T14:00:00+00:00",
                "temperature": 14.2,
            }
        ]
    }
    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "hourly"},
        blocking=True,
        return_response=True,
    )
    assert response == {
        "forecast": [
            {
                "condition": "cloudy",
                "datetime": "2023-02-17T14:00:00+00:00",
                "temperature": 14.2,
            }
        ]
    }
    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "twice_daily"},
        blocking=True,
        return_response=True,
    )
    assert response == {
        "forecast": [
            {
                "condition": "fog",
                "datetime": "2023-02-17T14:00:00+00:00",
                "temperature": 14.2,
                "is_daytime": True,
            }
        ]
    }

    hass.states.async_set(
        "weather.forecast",
        "sunny",
        {
            ATTR_FORECAST: [
                Forecast(
                    condition="cloudy",
                    datetime="2023-02-17T14:00:00+00:00",
                    temperature=16.9,
                )
            ]
        },
    )
    await hass.async_block_till_done()
    state = hass.states.get("weather.forecast")
    assert state is not None
    assert state.state == "sunny"

    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "daily"},
        blocking=True,
        return_response=True,
    )
    assert response == {
        "forecast": [
            {
                "condition": "cloudy",
                "datetime": "2023-02-17T14:00:00+00:00",
                "temperature": 16.9,
            }
        ]
    }


@pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)])
@pytest.mark.parametrize(
    "config",
    [
        {
            "weather": [
                {
                    "platform": "template",
                    "name": "forecast",
                    "condition_template": "sunny",
                    "forecast_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_daily_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_hourly_template": "{{ states.weather.forecast_hourly.attributes.forecast }}",
                    "temperature_template": "{{ states('sensor.temperature') | float }}",
                    "humidity_template": "{{ states('sensor.humidity') | int }}",
                },
            ]
        },
    ],
)
async def test_forecast_invalid(
    hass: HomeAssistant, start_ha, caplog: pytest.LogCaptureFixture
) -> None:
    """Test invalid forecasts."""
    for attr, _v_attr, value in [
        ("sensor.temperature", ATTR_WEATHER_TEMPERATURE, 22.3),
        ("sensor.humidity", ATTR_WEATHER_HUMIDITY, 60),
    ]:
        hass.states.async_set(attr, value)
        await hass.async_block_till_done()

    hass.states.async_set(
        "weather.forecast",
        "sunny",
        {
            ATTR_FORECAST: [
                Forecast(
                    condition="cloudy",
                    datetime="2023-02-17T14:00:00+00:00",
                    temperature=14.2,
                    not_correct=1,
                )
            ]
        },
    )
    hass.states.async_set(
        "weather.forecast_hourly",
        "sunny",
        {ATTR_FORECAST: None},
    )
    await hass.async_block_till_done()
    state = hass.states.get("weather.forecast_hourly")
    assert state is not None
    assert state.state == "sunny"

    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "daily"},
        blocking=True,
        return_response=True,
    )
    assert response == {"forecast": []}
    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "hourly"},
        blocking=True,
        return_response=True,
    )
    assert response == {"forecast": []}
    assert "Only valid keys in Forecast are allowed" in caplog.text


@pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)])
@pytest.mark.parametrize(
    "config",
    [
        {
            "weather": [
                {
                    "platform": "template",
                    "name": "forecast",
                    "condition_template": "sunny",
                    "forecast_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_twice_daily_template": "{{ states.weather.forecast_twice_daily.attributes.forecast }}",
                    "temperature_template": "{{ states('sensor.temperature') | float }}",
                    "humidity_template": "{{ states('sensor.humidity') | int }}",
                },
            ]
        },
    ],
)
async def test_forecast_invalid_is_daytime_missing_in_twice_daily(
    hass: HomeAssistant, start_ha, caplog: pytest.LogCaptureFixture
) -> None:
    """Test forecast service invalid when is_daytime missing in twice_daily forecast."""
    for attr, _v_attr, value in [
        ("sensor.temperature", ATTR_WEATHER_TEMPERATURE, 22.3),
        ("sensor.humidity", ATTR_WEATHER_HUMIDITY, 60),
    ]:
        hass.states.async_set(attr, value)
        await hass.async_block_till_done()

    hass.states.async_set(
        "weather.forecast_twice_daily",
        "sunny",
        {
            ATTR_FORECAST: [
                Forecast(
                    condition="cloudy",
                    datetime="2023-02-17T14:00:00+00:00",
                    temperature=14.2,
                )
            ]
        },
    )
    await hass.async_block_till_done()
    state = hass.states.get("weather.forecast_twice_daily")
    assert state is not None
    assert state.state == "sunny"

    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "twice_daily"},
        blocking=True,
        return_response=True,
    )
    assert response == {"forecast": []}
    assert "`is_daytime` is missing in twice_daily forecast" in caplog.text


@pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)])
@pytest.mark.parametrize(
    "config",
    [
        {
            "weather": [
                {
                    "platform": "template",
                    "name": "forecast",
                    "condition_template": "sunny",
                    "forecast_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_twice_daily_template": "{{ states.weather.forecast_twice_daily.attributes.forecast }}",
                    "temperature_template": "{{ states('sensor.temperature') | float }}",
                    "humidity_template": "{{ states('sensor.humidity') | int }}",
                },
            ]
        },
    ],
)
async def test_forecast_invalid_datetime_missing(
    hass: HomeAssistant, start_ha, caplog: pytest.LogCaptureFixture
) -> None:
    """Test forecast service invalid when datetime missing."""
    for attr, _v_attr, value in [
        ("sensor.temperature", ATTR_WEATHER_TEMPERATURE, 22.3),
        ("sensor.humidity", ATTR_WEATHER_HUMIDITY, 60),
    ]:
        hass.states.async_set(attr, value)
        await hass.async_block_till_done()

    hass.states.async_set(
        "weather.forecast_twice_daily",
        "sunny",
        {
            ATTR_FORECAST: [
                Forecast(
                    condition="cloudy",
                    temperature=14.2,
                    is_daytime=True,
                )
            ]
        },
    )
    await hass.async_block_till_done()
    state = hass.states.get("weather.forecast_twice_daily")
    assert state is not None
    assert state.state == "sunny"

    response = await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "twice_daily"},
        blocking=True,
        return_response=True,
    )
    assert response == {"forecast": []}
    assert "`datetime` is required in forecasts" in caplog.text


@pytest.mark.parametrize(("count", "domain"), [(1, WEATHER_DOMAIN)])
@pytest.mark.parametrize(
    "config",
    [
        {
            "weather": [
                {
                    "platform": "template",
                    "name": "forecast",
                    "condition_template": "sunny",
                    "forecast_template": "{{ states.weather.forecast.attributes.forecast }}",
                    "forecast_daily_template": "{{ states.weather.forecast_daily.attributes.forecast }}",
                    "forecast_hourly_template": "{{ states.weather.forecast_hourly.attributes.forecast }}",
                    "temperature_template": "{{ states('sensor.temperature') | float }}",
                    "humidity_template": "{{ states('sensor.humidity') | int }}",
                },
            ]
        },
    ],
)
async def test_forecast_format_error(
    hass: HomeAssistant, start_ha, caplog: pytest.LogCaptureFixture
) -> None:
    """Test forecast service invalid on incorrect format."""
    for attr, _v_attr, value in [
        ("sensor.temperature", ATTR_WEATHER_TEMPERATURE, 22.3),
        ("sensor.humidity", ATTR_WEATHER_HUMIDITY, 60),
    ]:
        hass.states.async_set(attr, value)
        await hass.async_block_till_done()

    hass.states.async_set(
        "weather.forecast_daily",
        "sunny",
        {
            ATTR_FORECAST: [
                "cloudy",
                "2023-02-17T14:00:00+00:00",
                14.2,
                1,
            ]
        },
    )
    hass.states.async_set(
        "weather.forecast_hourly",
        "sunny",
        {
            ATTR_FORECAST: {
                "condition": "cloudy",
                "temperature": 14.2,
                "is_daytime": True,
            }
        },
    )

    await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "daily"},
        blocking=True,
        return_response=True,
    )
    assert "Forecasts is not a list, see Weather documentation" in caplog.text
    await hass.services.async_call(
        WEATHER_DOMAIN,
        SERVICE_GET_FORECAST,
        {"entity_id": "weather.forecast", "type": "hourly"},
        blocking=True,
        return_response=True,
    )
    assert "Forecast in list is not a dict, see Weather documentation" in caplog.text
