"""Diagnostics support for tuya-local."""

from __future__ import annotations

from typing import Any

from homeassistant.components.diagnostics import REDACTED
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntry
from tinytuya import __version__ as tinytuya_version

from .const import (
    API_PROTOCOL_VERSIONS,
    CONF_DEVICE_CID,
    CONF_PROTOCOL_VERSION,
    CONF_TYPE,
    DOMAIN,
)
from .device import TuyaLocalDevice
from .helpers.config import get_device_id


async def async_get_config_entry_diagnostics(
    hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
    """Return diagnostics for a config entry."""
    return _async_get_diagnostics(hass, entry)


async def async_get_device_diagnostics(
    hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry
) -> dict[str, Any]:
    """Return diagnostics for a device entry."""
    return _async_get_diagnostics(hass, entry, device)


@callback
def _async_get_diagnostics(
    hass: HomeAssistant,
    entry: ConfigEntry,
    device: DeviceEntry | None = None,
) -> dict[str, Any]:
    """Return diagnostics for a tuya-local config entry."""
    hass_data = hass.data[DOMAIN][get_device_id(entry.data)]
    hostname = entry.data.get(CONF_HOST, "")

    data = {
        "name": entry.title,
        "type": entry.data[CONF_TYPE],
        "device_id": REDACTED,
        "device_cid": REDACTED if entry.data.get(CONF_DEVICE_CID, "") != "" else "",
        "local_key": REDACTED,
        "host": REDACTED
        if hostname != "" and hostname.casefold() != "auto"
        else hostname,
        "protocol_version": entry.data[CONF_PROTOCOL_VERSION],
        "tinytuya_version": tinytuya_version,
    }

    # The DeviceEntry also has interesting looking data, but this
    # integration does not publish anything to it other than some hardcoded
    # values that don't change between devices. Instead get the live data
    # from the running hass.
    data |= _async_device_as_dict(hass, hass_data["device"])

    return data


def redact_dps(device: TuyaLocalDevice, dps: dict[str, Any]) -> dict[str, Any]:
    """Redact any sensitive data from a list of dps"""
    sensitive = []
    for entity in device._children:
        for dp in entity._config.dps():
            if dp.sensitive:
                sensitive.append(dp.id)
    return {k: (REDACTED if k in sensitive else v) for (k, v) in dps.items()}


def redact_entity(
    device: TuyaLocalDevice,
    entity_id: str,
    state_dict: dict[str, Any],
) -> dict[str, Any]:
    sensitive = []
    for entity in device._children:
        if entity._config.config_id == entity_id:
            for dp in entity._config.dps():
                if dp.sensitive:
                    sensitive.append(dp.name)
    return {k: (REDACTED if k in sensitive else v) for (k, v) in state_dict.items()}


@callback
def _async_device_as_dict(
    hass: HomeAssistant, device: TuyaLocalDevice
) -> dict[str, Any]:
    """Represent a Tuya Local device as a dictionary."""

    # Base device information, without sensitive information
    data = {
        "name": device.name,
        "api_version_set": device._api.version,
        "api_version_used": (
            "none"
            if device._api_protocol_version_index is None
            else API_PROTOCOL_VERSIONS[device._api_protocol_version_index]
        ),
        "api_working": device._api_protocol_working,
        "status": device._api.dps_cache,
        "cached_state": redact_dps(device, device._cached_state),
        "pending_state": redact_dps(device, device._pending_updates),
        "connected": device._running,
        "force_dps": device._force_dps,
    }

    device_registry = dr.async_get(hass)
    entity_registry = er.async_get(hass)
    hass_device = device_registry.async_get_device(
        identifiers={(DOMAIN, device.unique_id)}
    )
    if hass_device:
        data["home_assistant"] = {
            "name": hass_device.name,
            "name_by_user": hass_device.name_by_user,
            "disabled": hass_device.disabled,
            "disabled_by": hass_device.disabled_by,
            "entities": [],
        }

        hass_entities = er.async_entries_for_device(
            entity_registry,
            device_id=hass_device.id,
            include_disabled_entities=True,
        )
        for entity_entry in hass_entities:
            state = hass.states.get(entity_entry.entity_id)
            state_dict = None
            if state:
                state_dict = redact_entity(
                    device,
                    entity_entry.entity_id,
                    state.as_dict(),
                )

                # Redact entity_picture in case it is sensitive
                if "entity_picture" in state_dict["attributes"]:
                    state_dict["attributes"] = {
                        **state_dict["attributes"],
                        "entity_picture": REDACTED,
                    }
                # Context is not useful information
                state_dict.pop("context", None)

            data["home_assistant"]["entities"].append(
                {
                    "disabled": entity_entry.disabled,
                    "disabled_by": entity_entry.disabled_by,
                    "entity_category": entity_entry.entity_category,
                    "device_class": entity_entry.device_class,
                    "original_device_class": entity_entry.original_device_class,
                    "icon": entity_entry.icon,
                    "unit_of_measurement": entity_entry.unit_of_measurement,
                    "state": state_dict,
                }
            )
    return data
