"""The tests for the Sun sensor platform."""

from datetime import datetime, timedelta

from astral import LocationInfo
import astral.sun
from freezegun.api import FrozenDateTimeFactory
import pytest

from homeassistant.components import sun
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util

from tests.common import async_fire_time_changed


@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_setting_rising(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test retrieving sun setting and rising."""
    utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
    freezer.move_to(utc_now)
    await async_setup_component(hass, sun.DOMAIN, {sun.DOMAIN: {}})
    await hass.async_block_till_done()

    utc_today = utc_now.date()

    location = LocationInfo(
        latitude=hass.config.latitude, longitude=hass.config.longitude
    )

    mod = -1
    while True:
        next_dawn = astral.sun.dawn(
            location.observer, date=utc_today + timedelta(days=mod)
        )
        if next_dawn > utc_now:
            break
        mod += 1

    mod = -1
    while True:
        next_dusk = astral.sun.dusk(
            location.observer, date=utc_today + timedelta(days=mod)
        )
        if next_dusk > utc_now:
            break
        mod += 1

    mod = -1
    while True:
        next_midnight = astral.sun.midnight(
            location.observer, date=utc_today + timedelta(days=mod)
        )
        if next_midnight > utc_now:
            break
        mod += 1

    mod = -1
    while True:
        next_noon = astral.sun.noon(
            location.observer, date=utc_today + timedelta(days=mod)
        )
        if next_noon > utc_now:
            break
        mod += 1

    mod = -1
    while True:
        next_rising = astral.sun.sunrise(
            location.observer, date=utc_today + timedelta(days=mod)
        )
        if next_rising > utc_now:
            break
        mod += 1

    mod = -1
    while True:
        next_setting = astral.sun.sunset(
            location.observer, date=utc_today + timedelta(days=mod)
        )
        if next_setting > utc_now:
            break
        mod += 1

    expected_solar_elevation = astral.sun.elevation(location.observer, utc_now)
    expected_solar_azimuth = astral.sun.azimuth(location.observer, utc_now)

    state1 = hass.states.get("sensor.sun_next_dawn")
    state2 = hass.states.get("sensor.sun_next_dusk")
    state3 = hass.states.get("sensor.sun_next_midnight")
    state4 = hass.states.get("sensor.sun_next_noon")
    state5 = hass.states.get("sensor.sun_next_rising")
    state6 = hass.states.get("sensor.sun_next_setting")
    assert next_dawn.replace(microsecond=0) == dt_util.parse_datetime(state1.state)
    assert next_dusk.replace(microsecond=0) == dt_util.parse_datetime(state2.state)
    assert next_midnight.replace(microsecond=0) == dt_util.parse_datetime(state3.state)
    assert next_noon.replace(microsecond=0) == dt_util.parse_datetime(state4.state)
    assert next_rising.replace(microsecond=0) == dt_util.parse_datetime(state5.state)
    assert next_setting.replace(microsecond=0) == dt_util.parse_datetime(state6.state)
    solar_elevation_state = hass.states.get("sensor.sun_solar_elevation")
    assert float(solar_elevation_state.state) == pytest.approx(
        expected_solar_elevation, 0.1
    )
    solar_azimuth_state = hass.states.get("sensor.sun_solar_azimuth")
    assert float(solar_azimuth_state.state) == pytest.approx(
        expected_solar_azimuth, 0.1
    )

    entry_ids = hass.config_entries.async_entries("sun")

    entity = entity_registry.async_get("sensor.sun_next_dawn")

    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-next_dawn"

    freezer.tick(timedelta(hours=24))
    # Block once for Sun to update
    await hass.async_block_till_done()
    # Block another time for the sensors to update
    await hass.async_block_till_done()

    # Make sure all the signals work
    assert state1.state != hass.states.get("sensor.sun_next_dawn").state
    assert state2.state != hass.states.get("sensor.sun_next_dusk").state
    assert state3.state != hass.states.get("sensor.sun_next_midnight").state
    assert state4.state != hass.states.get("sensor.sun_next_noon").state
    assert state5.state != hass.states.get("sensor.sun_next_rising").state
    assert state6.state != hass.states.get("sensor.sun_next_setting").state
    assert (
        solar_elevation_state.state
        != hass.states.get("sensor.sun_solar_elevation").state
    )
    assert (
        solar_azimuth_state.state != hass.states.get("sensor.sun_solar_azimuth").state
    )

    entity = entity_registry.async_get("sensor.sun_next_dusk")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-next_dusk"

    entity = entity_registry.async_get("sensor.sun_next_midnight")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-next_midnight"

    entity = entity_registry.async_get("sensor.sun_next_noon")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-next_noon"

    entity = entity_registry.async_get("sensor.sun_next_rising")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-next_rising"

    entity = entity_registry.async_get("sensor.sun_next_setting")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-next_setting"

    entity = entity_registry.async_get("sensor.sun_solar_elevation")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-solar_elevation"

    entity = entity_registry.async_get("sensor.sun_solar_azimuth")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-solar_azimuth"

    entity = entity_registry.async_get("sensor.sun_solar_rising")
    assert entity
    assert entity.entity_category is EntityCategory.DIAGNOSTIC
    assert entity.unique_id == f"{entry_ids[0].entry_id}-solar_rising"


@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_deprecation(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    issue_registry: ir.IssueRegistry,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test sensor.sun_solar_rising deprecation."""
    utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
    freezer.move_to(utc_now)
    await async_setup_component(hass, sun.DOMAIN, {sun.DOMAIN: {}})
    await hass.async_block_till_done()

    assert issue_registry.async_get_issue(
        domain="sun",
        issue_id="deprecated_sun_solar_rising",
    )
    assert len(issue_registry.issues) == 1

    entity_registry.async_update_entity(
        "sensor.sun_solar_rising", disabled_by=er.RegistryEntryDisabler.USER
    )
    await hass.async_block_till_done()

    assert not issue_registry.async_get_issue(
        domain="sun",
        issue_id="deprecated_sun_solar_rising",
    )
    assert len(issue_registry.issues) == 0

    entity_registry.async_update_entity("sensor.sun_solar_rising", disabled_by=None)
    await hass.async_block_till_done()
    freezer.tick(delta=RELOAD_AFTER_UPDATE_DELAY)
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    assert issue_registry.async_get_issue(
        domain="sun",
        issue_id="deprecated_sun_solar_rising",
    )
    assert len(issue_registry.issues) == 1