"""LCN Websocket Tests."""

from typing import Any

from pypck.lcn_addr import LcnAddr
import pytest

from homeassistant.components.lcn import AddressType
from homeassistant.components.lcn.const import CONF_DOMAIN_DATA
from homeassistant.components.lcn.helpers import get_device_config
from homeassistant.const import (
    CONF_ADDRESS,
    CONF_DEVICES,
    CONF_DOMAIN,
    CONF_ENTITIES,
    CONF_NAME,
    CONF_TYPE,
)
from homeassistant.core import HomeAssistant

from .conftest import MockConfigEntry, init_integration

from tests.typing import WebSocketGenerator

DEVICES_PAYLOAD = {CONF_TYPE: "lcn/devices", "entry_id": ""}
ENTITIES_PAYLOAD = {
    CONF_TYPE: "lcn/entities",
    "entry_id": "",
}
SCAN_PAYLOAD = {CONF_TYPE: "lcn/devices/scan", "entry_id": ""}
DEVICES_ADD_PAYLOAD = {
    CONF_TYPE: "lcn/devices/add",
    "entry_id": "",
    CONF_ADDRESS: (0, 10, False),
}
DEVICES_DELETE_PAYLOAD = {
    CONF_TYPE: "lcn/devices/delete",
    "entry_id": "",
    CONF_ADDRESS: (0, 7, False),
}
ENTITIES_ADD_PAYLOAD = {
    CONF_TYPE: "lcn/entities/add",
    "entry_id": "",
    CONF_ADDRESS: (0, 7, False),
    CONF_NAME: "test_switch",
    CONF_DOMAIN: "switch",
    CONF_DOMAIN_DATA: {"output": "RELAY5"},
}
ENTITIES_DELETE_PAYLOAD = {
    CONF_TYPE: "lcn/entities/delete",
    "entry_id": "",
    CONF_ADDRESS: (0, 7, False),
    CONF_DOMAIN: "switch",
    CONF_DOMAIN_DATA: {"output": "RELAY1"},
}


async def test_lcn_devices_command(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator, entry: MockConfigEntry
) -> None:
    """Test lcn/devices command."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    await client.send_json_auto_id({**DEVICES_PAYLOAD, "entry_id": entry.entry_id})

    res = await client.receive_json()
    assert res["success"], res
    assert len(res["result"]) == len(entry.data[CONF_DEVICES])
    assert all(
        {**result, CONF_ADDRESS: tuple(result[CONF_ADDRESS])}
        in entry.data[CONF_DEVICES]
        for result in res["result"]
    )


@pytest.mark.parametrize(
    "payload",
    [
        ENTITIES_PAYLOAD,
        {**ENTITIES_PAYLOAD, CONF_ADDRESS: (0, 7, False)},
    ],
)
async def test_lcn_entities_command(
    hass: HomeAssistant,
    hass_ws_client: WebSocketGenerator,
    entry: MockConfigEntry,
    payload,
) -> None:
    """Test lcn/entities command."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    await client.send_json_auto_id(
        {
            **payload,
            "entry_id": entry.entry_id,
        }
    )

    res = await client.receive_json()
    assert res["success"], res
    entities = [
        entity
        for entity in entry.data[CONF_ENTITIES]
        if CONF_ADDRESS not in payload or entity[CONF_ADDRESS] == payload[CONF_ADDRESS]
    ]
    assert len(res["result"]) == len(entities)
    assert all(
        {**result, CONF_ADDRESS: tuple(result[CONF_ADDRESS])} in entities
        for result in res["result"]
    )


async def test_lcn_devices_scan_command(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator, entry: MockConfigEntry
) -> None:
    """Test lcn/devices/scan command."""
    # add new module which is not stored in config_entry
    lcn_connection = await init_integration(hass, entry)
    lcn_connection.get_device_connection(LcnAddr(0, 10, False))

    client = await hass_ws_client(hass)
    await client.send_json_auto_id({**SCAN_PAYLOAD, "entry_id": entry.entry_id})

    res = await client.receive_json()
    assert res["success"], res

    lcn_connection.scan_modules.assert_awaited()
    assert len(res["result"]) == len(entry.data[CONF_DEVICES])
    assert all(
        {**result, CONF_ADDRESS: tuple(result[CONF_ADDRESS])}
        in entry.data[CONF_DEVICES]
        for result in res["result"]
    )


async def test_lcn_devices_add_command(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator, entry: MockConfigEntry
) -> None:
    """Test lcn/devices/add command."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    assert get_device_config((0, 10, False), entry) is None

    await client.send_json_auto_id({**DEVICES_ADD_PAYLOAD, "entry_id": entry.entry_id})

    res = await client.receive_json()
    assert res["success"], res

    assert get_device_config((0, 10, False), entry)


async def test_lcn_devices_delete_command(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator, entry: MockConfigEntry
) -> None:
    """Test lcn/devices/delete command."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    assert get_device_config((0, 7, False), entry)

    await client.send_json_auto_id(
        {**DEVICES_DELETE_PAYLOAD, "entry_id": entry.entry_id}
    )

    res = await client.receive_json()
    assert res["success"], res
    assert get_device_config((0, 7, False), entry) is None


async def test_lcn_entities_add_command(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator, entry: MockConfigEntry
) -> None:
    """Test lcn/entities/add command."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)

    entity_config = {
        key: ENTITIES_ADD_PAYLOAD[key]
        for key in (CONF_ADDRESS, CONF_NAME, CONF_DOMAIN, CONF_DOMAIN_DATA)
    }

    assert entity_config not in entry.data[CONF_ENTITIES]

    await client.send_json_auto_id({**ENTITIES_ADD_PAYLOAD, "entry_id": entry.entry_id})

    res = await client.receive_json()
    assert res["success"], res

    assert entity_config in entry.data[CONF_ENTITIES]

    # invalid domain
    await client.send_json_auto_id(
        {**ENTITIES_ADD_PAYLOAD, "entry_id": entry.entry_id, CONF_DOMAIN: "invalid"}
    )

    res = await client.receive_json()
    assert not res["success"]
    assert res["error"]["code"] == "home_assistant_error"
    assert res["error"]["translation_key"] == "invalid_domain"


async def test_lcn_entities_delete_command(
    hass: HomeAssistant, hass_ws_client: WebSocketGenerator, entry: MockConfigEntry
) -> None:
    """Test lcn/entities/delete command."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)

    assert (
        len(
            [
                entity
                for entity in entry.data[CONF_ENTITIES]
                if entity[CONF_ADDRESS] == ENTITIES_DELETE_PAYLOAD[CONF_ADDRESS]
                and entity[CONF_DOMAIN] == ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN]
                and entity[CONF_DOMAIN_DATA]
                == ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN_DATA]
            ]
        )
        == 1
    )

    await client.send_json_auto_id(
        {**ENTITIES_DELETE_PAYLOAD, "entry_id": entry.entry_id}
    )

    res = await client.receive_json()
    assert res["success"], res

    assert (
        len(
            [
                entity
                for entity in entry.data[CONF_ENTITIES]
                if entity[CONF_ADDRESS] == ENTITIES_DELETE_PAYLOAD[CONF_ADDRESS]
                and entity[CONF_DOMAIN] == ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN]
                and entity[CONF_DOMAIN_DATA]
                == ENTITIES_DELETE_PAYLOAD[CONF_DOMAIN_DATA]
            ]
        )
        == 0
    )


@pytest.mark.parametrize(
    ("payload", "entity_id", "result"),
    [
        (DEVICES_PAYLOAD, "12345", False),
        (ENTITIES_PAYLOAD, "12345", False),
        (SCAN_PAYLOAD, "12345", False),
        (DEVICES_ADD_PAYLOAD, "12345", False),
        (DEVICES_DELETE_PAYLOAD, "12345", False),
        (ENTITIES_ADD_PAYLOAD, "12345", False),
        (ENTITIES_DELETE_PAYLOAD, "12345", False),
    ],
)
async def test_lcn_command_host_error(
    hass: HomeAssistant,
    hass_ws_client: WebSocketGenerator,
    entry: MockConfigEntry,
    payload: dict[str, str],
    entity_id: str,
    result: bool,
) -> None:
    """Test lcn commands for unknown host."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    await client.send_json_auto_id({**payload, "entry_id": entity_id})

    res = await client.receive_json()
    assert res["success"], res
    assert res["result"] == result


@pytest.mark.parametrize(
    ("payload", "address", "result"),
    [
        (DEVICES_ADD_PAYLOAD, (0, 7, False), False),  # device already existing
        (DEVICES_DELETE_PAYLOAD, (0, 42, False), False),
        (ENTITIES_ADD_PAYLOAD, (0, 42, False), False),
        (ENTITIES_DELETE_PAYLOAD, (0, 42, 0), False),
    ],
)
async def test_lcn_command_address_error(
    hass: HomeAssistant,
    hass_ws_client: WebSocketGenerator,
    entry: MockConfigEntry,
    payload: dict[str, Any],
    address: AddressType,
    result: bool,
) -> None:
    """Test lcn commands for address error."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    await client.send_json_auto_id(
        {**payload, "entry_id": entry.entry_id, CONF_ADDRESS: address}
    )

    res = await client.receive_json()
    assert res["success"], res
    assert res["result"] == result


async def test_lcn_entities_add_existing_error(
    hass: HomeAssistant,
    hass_ws_client: WebSocketGenerator,
    entry: MockConfigEntry,
) -> None:
    """Test lcn commands for address error."""
    await init_integration(hass, entry)

    client = await hass_ws_client(hass)
    await client.send_json_auto_id(
        {
            **ENTITIES_ADD_PAYLOAD,
            "entry_id": entry.entry_id,
            CONF_DOMAIN_DATA: {"output": "RELAY1"},
        }
    )

    res = await client.receive_json()
    assert res["success"], res
    assert res["result"] is False
