"""Functions used to migrate unique IDs for Z-Wave JS entities."""
from __future__ import annotations

import logging

from zwave_js_server.client import Client as ZwaveClient
from zwave_js_server.model.value import Value as ZwaveValue

from homeassistant.core import callback
from homeassistant.helpers.entity_registry import EntityRegistry

from .const import DOMAIN
from .discovery import ZwaveDiscoveryInfo
from .helpers import get_unique_id

_LOGGER = logging.getLogger(__name__)


@callback
def async_migrate_entity(
    ent_reg: EntityRegistry, platform: str, old_unique_id: str, new_unique_id: str
) -> None:
    """Check if entity with old unique ID exists, and if so migrate it to new ID."""
    if entity_id := ent_reg.async_get_entity_id(platform, DOMAIN, old_unique_id):
        _LOGGER.debug(
            "Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
            entity_id,
            old_unique_id,
            new_unique_id,
        )
        try:
            ent_reg.async_update_entity(
                entity_id,
                new_unique_id=new_unique_id,
            )
        except ValueError:
            _LOGGER.debug(
                (
                    "Entity %s can't be migrated because the unique ID is taken. "
                    "Cleaning it up since it is likely no longer valid."
                ),
                entity_id,
            )
            ent_reg.async_remove(entity_id)


@callback
def async_migrate_discovered_value(
    ent_reg: EntityRegistry, client: ZwaveClient, disc_info: ZwaveDiscoveryInfo
) -> None:
    """Migrate unique ID for entity/entities tied to discovered value."""
    new_unique_id = get_unique_id(
        client.driver.controller.home_id,
        disc_info.primary_value.value_id,
    )

    # 2021.2.*, 2021.3.0b0, and 2021.3.0 formats
    for value_id in get_old_value_ids(disc_info.primary_value):
        old_unique_id = get_unique_id(
            client.driver.controller.home_id,
            value_id,
        )
        # Most entities have the same ID format, but notification binary sensors
        # have a state key in their ID so we need to handle them differently
        if (
            disc_info.platform == "binary_sensor"
            and disc_info.platform_hint == "notification"
        ):
            for state_key in disc_info.primary_value.metadata.states:
                # ignore idle key (0)
                if state_key == "0":
                    continue

                async_migrate_entity(
                    ent_reg,
                    disc_info.platform,
                    f"{old_unique_id}.{state_key}",
                    f"{new_unique_id}.{state_key}",
                )

            # Once we've iterated through all state keys, we can move on to the
            # next item
            continue

        async_migrate_entity(ent_reg, disc_info.platform, old_unique_id, new_unique_id)


@callback
def get_old_value_ids(value: ZwaveValue) -> list[str]:
    """Get old value IDs so we can migrate entity unique ID."""
    value_ids = []

    # Pre 2021.3.0 value ID
    command_class = value.command_class
    endpoint = value.endpoint or "00"
    property_ = value.property_
    property_key_name = value.property_key_name or "00"
    value_ids.append(
        f"{value.node.node_id}.{value.node.node_id}-{command_class}-{endpoint}-"
        f"{property_}-{property_key_name}"
    )

    endpoint = "00" if value.endpoint is None else value.endpoint
    property_key = "00" if value.property_key is None else value.property_key
    property_key_name = value.property_key_name or "00"

    value_id = (
        f"{value.node.node_id}-{command_class}-{endpoint}-"
        f"{property_}-{property_key}-{property_key_name}"
    )
    # 2021.3.0b0 and 2021.3.0 value IDs
    value_ids.extend([f"{value.node.node_id}.{value_id}", value_id])

    return value_ids