"""Fixtures for the Velbus tests."""

from collections.abc import Generator
from unittest.mock import AsyncMock, patch

import pytest
from velbusaio.channels import (
    Blind,
    Button,
    ButtonCounter,
    Dimmer,
    LightSensor,
    Relay,
    SelectedProgram,
    SensorNumber,
    Temperature,
)
from velbusaio.module import Module

from homeassistant.components.velbus import VelbusConfigEntry
from homeassistant.components.velbus.const import DOMAIN
from homeassistant.const import CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant

from .const import PORT_TCP

from tests.common import MockConfigEntry


@pytest.fixture(name="controller")
def mock_controller(
    mock_button: AsyncMock,
    mock_relay: AsyncMock,
    mock_temperature: AsyncMock,
    mock_select: AsyncMock,
    mock_buttoncounter: AsyncMock,
    mock_sensornumber: AsyncMock,
    mock_lightsensor: AsyncMock,
    mock_dimmer: AsyncMock,
    mock_module_no_subdevices: AsyncMock,
    mock_module_subdevices: AsyncMock,
    mock_cover: AsyncMock,
    mock_cover_no_position: AsyncMock,
) -> Generator[AsyncMock]:
    """Mock a successful velbus controller."""
    with (
        patch("homeassistant.components.velbus.Velbus", autospec=True) as controller,
        patch(
            "homeassistant.components.velbus.config_flow.velbusaio.controller.Velbus",
            new=controller,
        ),
    ):
        cont = controller.return_value
        cont.get_all_binary_sensor.return_value = [mock_button]
        cont.get_all_button.return_value = [mock_button]
        cont.get_all_switch.return_value = [mock_relay]
        cont.get_all_climate.return_value = [mock_temperature]
        cont.get_all_select.return_value = [mock_select]
        cont.get_all_sensor.return_value = [
            mock_buttoncounter,
            mock_temperature,
            mock_sensornumber,
            mock_lightsensor,
        ]
        cont.get_all_light.return_value = [mock_dimmer]
        cont.get_all_led.return_value = [mock_button]
        cont.get_all_cover.return_value = [mock_cover, mock_cover_no_position]
        cont.get_modules.return_value = {
            1: mock_module_no_subdevices,
            2: mock_module_no_subdevices,
            3: mock_module_no_subdevices,
            4: mock_module_no_subdevices,
            99: mock_module_subdevices,
        }
        cont.get_module.return_value = mock_module_subdevices
        yield controller


@pytest.fixture
def mock_module_no_subdevices(
    mock_relay: AsyncMock,
) -> AsyncMock:
    """Mock a velbus module."""
    module = AsyncMock(spec=Module)
    module.get_type_name.return_value = "VMB4RYLD"
    module.get_addresses.return_value = [1, 2, 3, 4]
    module.get_name.return_value = "BedRoom"
    module.get_serial.return_value = "a1b2c3d4e5f6"
    module.get_sw_version.return_value = "1.0.0"
    module.is_loaded.return_value = True
    module.get_channels.return_value = {}
    return module


@pytest.fixture
def mock_module_subdevices() -> AsyncMock:
    """Mock a velbus module."""
    module = AsyncMock(spec=Module)
    module.get_type_name.return_value = "VMB2BLE"
    module.get_addresses.return_value = [88]
    module.get_name.return_value = "Kitchen"
    module.get_serial.return_value = "a1b2c3d4e5f6"
    module.get_sw_version.return_value = "2.0.0"
    module.is_loaded.return_value = True
    module.get_channels.return_value = {}
    return module


@pytest.fixture
def mock_button() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=Button)
    channel.get_categories.return_value = ["binary_sensor", "led", "button"]
    channel.get_name.return_value = "ButtonOn"
    channel.get_module_address.return_value = 1
    channel.get_channel_number.return_value = 1
    channel.get_module_type_name.return_value = "VMB4RYLD"
    channel.get_module_type.return_value = 99
    channel.get_full_name.return_value = "Bedroom kid 1"
    channel.get_module_sw_version.return_value = "1.0.0"
    channel.get_module_serial.return_value = "a1b2c3d4e5f6"
    channel.is_sub_device.return_value = False
    channel.is_closed.return_value = True
    channel.is_on.return_value = False
    return channel


@pytest.fixture
def mock_temperature() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=Temperature)
    channel.get_categories.return_value = ["sensor", "climate"]
    channel.get_name.return_value = "Temperature"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 3
    channel.get_module_type_name.return_value = "VMB4GPO"
    channel.get_full_name.return_value = "Living room"
    channel.get_module_sw_version.return_value = "3.0.0"
    channel.get_module_serial.return_value = "asdfghjk"
    channel.get_module_type.return_value = 1
    channel.is_sub_device.return_value = False
    channel.is_counter_channel.return_value = False
    channel.get_class.return_value = "temperature"
    channel.get_unit.return_value = "°C"
    channel.get_state.return_value = 20.0
    channel.is_temperature.return_value = True
    channel.get_max.return_value = 30.0
    channel.get_min.return_value = 10.0
    channel.get_climate_target.return_value = 21.0
    channel.get_climate_preset.return_value = 1
    channel.get_climate_mode.return_value = "day"
    channel.get_cool_mode.return_value = "heat"
    return channel


@pytest.fixture
def mock_relay() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=Relay)
    channel.get_categories.return_value = ["switch"]
    channel.get_name.return_value = "RelayName"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 55
    channel.get_module_type_name.return_value = "VMB4RYNO"
    channel.get_full_name.return_value = "Living room"
    channel.get_module_sw_version.return_value = "1.0.1"
    channel.get_module_serial.return_value = "qwerty123"
    channel.get_module_type.return_value = 2
    channel.is_sub_device.return_value = True
    channel.is_on.return_value = True
    return channel


@pytest.fixture
def mock_select() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=SelectedProgram)
    channel.get_categories.return_value = ["select"]
    channel.get_name.return_value = "select"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 33
    channel.get_module_type_name.return_value = "VMB4RYNO"
    channel.get_module_type.return_value = 3
    channel.get_full_name.return_value = "Kitchen"
    channel.get_module_sw_version.return_value = "1.1.1"
    channel.get_module_serial.return_value = "qwerty1234567"
    channel.is_sub_device.return_value = False
    channel.get_options.return_value = ["none", "summer", "winter", "holiday"]
    channel.get_selected_program.return_value = "winter"
    return channel


@pytest.fixture
def mock_buttoncounter() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=ButtonCounter)
    channel.get_categories.return_value = ["sensor"]
    channel.get_name.return_value = "ButtonCounter"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 2
    channel.get_module_type_name.return_value = "VMB7IN"
    channel.get_module_type.return_value = 4
    channel.get_full_name.return_value = "Input"
    channel.get_module_sw_version.return_value = "1.0.0"
    channel.get_module_serial.return_value = "a1b2c3d4e5f6"
    channel.is_sub_device.return_value = True
    channel.is_counter_channel.return_value = True
    channel.is_temperature.return_value = False
    channel.get_state.return_value = 100
    channel.get_unit.return_value = "W"
    channel.get_counter_state.return_value = 100
    channel.get_counter_unit.return_value = "kWh"
    return channel


@pytest.fixture
def mock_sensornumber() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=SensorNumber)
    channel.get_categories.return_value = ["sensor"]
    channel.get_name.return_value = "SensorNumber"
    channel.get_module_address.return_value = 2
    channel.get_channel_number.return_value = 3
    channel.get_module_type_name.return_value = "VMB7IN"
    channel.get_module_type.return_value = 8
    channel.get_full_name.return_value = "Input"
    channel.get_module_sw_version.return_value = "1.0.0"
    channel.get_module_serial.return_value = "a1b2c3d4e5f6"
    channel.is_sub_device.return_value = False
    channel.is_counter_channel.return_value = False
    channel.is_temperature.return_value = False
    channel.get_unit.return_value = "m"
    channel.get_state.return_value = 10
    return channel


@pytest.fixture
def mock_lightsensor() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=LightSensor)
    channel.get_categories.return_value = ["sensor"]
    channel.get_name.return_value = "LightSensor"
    channel.get_module_address.return_value = 2
    channel.get_channel_number.return_value = 4
    channel.get_module_type_name.return_value = "VMB7IN"
    channel.get_module_type.return_value = 8
    channel.get_full_name.return_value = "Input"
    channel.get_module_sw_version.return_value = "1.0.0"
    channel.get_module_serial.return_value = "a1b2c3d4e5f6"
    channel.is_sub_device.return_value = False
    channel.is_counter_channel.return_value = False
    channel.is_temperature.return_value = False
    channel.get_unit.return_value = "illuminance"
    channel.get_state.return_value = 250
    return channel


@pytest.fixture
def mock_dimmer() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=Dimmer)
    channel.get_categories.return_value = ["light"]
    channel.get_name.return_value = "Dimmer"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 10
    channel.get_module_type_name.return_value = "VMBDN1"
    channel.get_module_type.return_value = 9
    channel.get_full_name.return_value = "Dimmer full name"
    channel.get_module_sw_version.return_value = "1.0.0"
    channel.get_module_serial.return_value = "a1b2c3d4e5f6g7"
    channel.is_sub_device.return_value = True
    channel.is_on.return_value = False
    channel.get_dimmer_state.return_value = 33
    return channel


@pytest.fixture
def mock_cover() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=Blind)
    channel.get_categories.return_value = ["cover"]
    channel.get_name.return_value = "CoverName"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 9
    channel.get_module_type_name.return_value = "VMB2BLE"
    channel.get_module_type.return_value = 10
    channel.get_full_name.return_value = "Basement"
    channel.get_module_sw_version.return_value = "1.0.1"
    channel.get_module_serial.return_value = "1234"
    channel.is_sub_device.return_value = True
    channel.support_position.return_value = True
    channel.get_position.return_value = 50
    channel.is_closed.return_value = False
    channel.is_open.return_value = True
    channel.is_opening.return_value = False
    channel.is_closing.return_value = False
    return channel


@pytest.fixture
def mock_cover_no_position() -> AsyncMock:
    """Mock a successful velbus channel."""
    channel = AsyncMock(spec=Blind)
    channel.get_categories.return_value = ["cover"]
    channel.get_name.return_value = "CoverNameNoPos"
    channel.get_module_address.return_value = 88
    channel.get_channel_number.return_value = 11
    channel.get_module_type_name.return_value = "VMB2BLE"
    channel.get_module_type.return_value = 10
    channel.get_full_name.return_value = "Basement"
    channel.get_module_sw_version.return_value = "1.0.1"
    channel.get_module_serial.return_value = "12345"
    channel.is_sub_device.return_value = True
    channel.support_position.return_value = False
    channel.get_position.return_value = None
    channel.is_closed.return_value = False
    channel.is_open.return_value = True
    channel.is_opening.return_value = True
    channel.is_closing.return_value = True
    return channel


@pytest.fixture(name="config_entry")
async def mock_config_entry(
    hass: HomeAssistant,
    controller: AsyncMock,
) -> VelbusConfigEntry:
    """Create and register mock config entry."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        data={CONF_PORT: PORT_TCP, CONF_NAME: "velbus home"},
    )
    config_entry.add_to_hass(hass)
    return config_entry