"""Tests for the reload helper."""
import logging
from unittest.mock import AsyncMock, Mock, patch

import pytest

from homeassistant import config
from homeassistant.const import SERVICE_RELOAD
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.helpers.reload import (
    async_get_platform_without_config_entry,
    async_integration_yaml_config,
    async_reload_integration_platforms,
    async_setup_reload_service,
)
from homeassistant.loader import async_get_integration

from tests.common import (
    MockModule,
    MockPlatform,
    get_fixture_path,
    mock_entity_platform,
    mock_integration,
)

_LOGGER = logging.getLogger(__name__)
DOMAIN = "test_domain"
PLATFORM = "test_platform"


async def test_reload_platform(hass):
    """Test the polling of only updated entities."""
    component_setup = Mock(return_value=True)

    setup_called = []

    async def setup_platform(*args):
        setup_called.append(args)

    mock_integration(hass, MockModule(DOMAIN, setup=component_setup))
    mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN]))

    mock_platform = MockPlatform(async_setup_platform=setup_platform)
    mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    await component.async_setup({DOMAIN: {"platform": PLATFORM, "sensors": None}})
    await hass.async_block_till_done()
    assert component_setup.called

    assert f"{DOMAIN}.{PLATFORM}" in hass.config.components
    assert len(setup_called) == 1

    platform = async_get_platform_without_config_entry(hass, PLATFORM, DOMAIN)
    assert platform.platform_name == PLATFORM
    assert platform.domain == DOMAIN

    yaml_path = get_fixture_path("helpers/reload_configuration.yaml")
    with patch.object(config, "YAML_CONFIG_FILE", yaml_path):
        await async_reload_integration_platforms(hass, PLATFORM, [DOMAIN])

    assert len(setup_called) == 2

    existing_platforms = async_get_platforms(hass, PLATFORM)
    for existing_platform in existing_platforms:
        existing_platform.config_entry = "abc"
    assert not async_get_platform_without_config_entry(hass, PLATFORM, DOMAIN)


async def test_setup_reload_service(hass):
    """Test setting up a reload service."""
    component_setup = Mock(return_value=True)

    setup_called = []

    async def setup_platform(*args):
        setup_called.append(args)

    mock_integration(hass, MockModule(DOMAIN, setup=component_setup))
    mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN]))

    mock_platform = MockPlatform(async_setup_platform=setup_platform)
    mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    await component.async_setup({DOMAIN: {"platform": PLATFORM, "sensors": None}})
    await hass.async_block_till_done()
    assert component_setup.called

    assert f"{DOMAIN}.{PLATFORM}" in hass.config.components
    assert len(setup_called) == 1

    await async_setup_reload_service(hass, PLATFORM, [DOMAIN])

    yaml_path = get_fixture_path("helpers/reload_configuration.yaml")
    with patch.object(config, "YAML_CONFIG_FILE", yaml_path):
        await hass.services.async_call(
            PLATFORM,
            SERVICE_RELOAD,
            {},
            blocking=True,
        )
        await hass.async_block_till_done()

    assert len(setup_called) == 2


async def test_setup_reload_service_when_async_process_component_config_fails(hass):
    """Test setting up a reload service with the config processing failing."""
    component_setup = Mock(return_value=True)

    setup_called = []

    async def setup_platform(*args):
        setup_called.append(args)

    mock_integration(hass, MockModule(DOMAIN, setup=component_setup))
    mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN]))

    mock_platform = MockPlatform(async_setup_platform=setup_platform)
    mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    await component.async_setup({DOMAIN: {"platform": PLATFORM, "sensors": None}})
    await hass.async_block_till_done()
    assert component_setup.called

    assert f"{DOMAIN}.{PLATFORM}" in hass.config.components
    assert len(setup_called) == 1

    await async_setup_reload_service(hass, PLATFORM, [DOMAIN])

    yaml_path = get_fixture_path("helpers/reload_configuration.yaml")
    with patch.object(config, "YAML_CONFIG_FILE", yaml_path), patch.object(
        config, "async_process_component_config", return_value=None
    ):
        await hass.services.async_call(
            PLATFORM,
            SERVICE_RELOAD,
            {},
            blocking=True,
        )
        await hass.async_block_till_done()

    assert len(setup_called) == 1


async def test_setup_reload_service_with_platform_that_provides_async_reset_platform(
    hass,
):
    """Test setting up a reload service using a platform that has its own async_reset_platform."""
    component_setup = AsyncMock(return_value=True)

    setup_called = []
    async_reset_platform_called = []

    async def setup_platform(*args):
        setup_called.append(args)

    async def async_reset_platform(*args):
        async_reset_platform_called.append(args)

    mock_integration(hass, MockModule(DOMAIN, async_setup=component_setup))
    integration = await async_get_integration(hass, DOMAIN)
    integration.get_component().async_reset_platform = async_reset_platform

    mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN]))

    mock_platform = MockPlatform(async_setup_platform=setup_platform)
    mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    await component.async_setup({DOMAIN: {"platform": PLATFORM, "name": "xyz"}})
    await hass.async_block_till_done()
    assert component_setup.called

    assert f"{DOMAIN}.{PLATFORM}" in hass.config.components
    assert len(setup_called) == 1

    await async_setup_reload_service(hass, PLATFORM, [DOMAIN])

    yaml_path = get_fixture_path("helpers/reload_configuration.yaml")
    with patch.object(config, "YAML_CONFIG_FILE", yaml_path):
        await hass.services.async_call(
            PLATFORM,
            SERVICE_RELOAD,
            {},
            blocking=True,
        )
        await hass.async_block_till_done()

    assert len(setup_called) == 1
    assert len(async_reset_platform_called) == 1


async def test_async_integration_yaml_config(hass):
    """Test loading yaml config for an integration."""
    mock_integration(hass, MockModule(DOMAIN))

    yaml_path = get_fixture_path(f"helpers/{DOMAIN}_configuration.yaml")
    with patch.object(config, "YAML_CONFIG_FILE", yaml_path):
        processed_config = await async_integration_yaml_config(hass, DOMAIN)

    assert processed_config == {DOMAIN: [{"name": "one"}, {"name": "two"}]}


async def test_async_integration_missing_yaml_config(hass):
    """Test loading missing yaml config for an integration."""
    mock_integration(hass, MockModule(DOMAIN))

    yaml_path = get_fixture_path("helpers/does_not_exist_configuration.yaml")
    with pytest.raises(FileNotFoundError), patch.object(
        config, "YAML_CONFIG_FILE", yaml_path
    ):
        await async_integration_yaml_config(hass, DOMAIN)