"""Diagnostics support for VeSync."""
from __future__ import annotations

from typing import Any

from pyvesync import VeSync

from homeassistant.components.diagnostics import REDACTED
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntry

from .common import VeSyncBaseDevice
from .const import DOMAIN, VS_MANAGER

KEYS_TO_REDACT = {"manager", "uuid", "mac_id"}


async def async_get_config_entry_diagnostics(
    hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
    """Return diagnostics for a config entry."""
    manager: VeSync = hass.data[DOMAIN][VS_MANAGER]

    data = {
        DOMAIN: {
            "bulb_count": len(manager.bulbs),
            "fan_count": len(manager.fans),
            "outlets_count": len(manager.outlets),
            "switch_count": len(manager.switches),
            "timezone": manager.time_zone,
        },
        "devices": {
            "bulbs": [_redact_device_values(device) for device in manager.bulbs],
            "fans": [_redact_device_values(device) for device in manager.fans],
            "outlets": [_redact_device_values(device) for device in manager.outlets],
            "switches": [_redact_device_values(device) for device in manager.switches],
        },
    }

    return data


async def async_get_device_diagnostics(
    hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry
) -> dict[str, Any]:
    """Return diagnostics for a device entry."""
    manager: VeSync = hass.data[DOMAIN][VS_MANAGER]
    device_dict = _build_device_dict(manager)
    vesync_device_id = next(iden[1] for iden in device.identifiers if iden[0] == DOMAIN)

    # Base device information, without sensitive information.
    data = _redact_device_values(device_dict[vesync_device_id])

    data["home_assistant"] = {
        "name": device.name,
        "name_by_user": device.name_by_user,
        "disabled": device.disabled,
        "disabled_by": device.disabled_by,
        "entities": [],
    }

    # Gather information how this VeSync device is represented in Home Assistant
    entity_registry = er.async_get(hass)
    hass_entities = er.async_entries_for_device(
        entity_registry,
        device_id=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 = dict(state.as_dict())
            # The context doesn't provide useful information in this case.
            state_dict.pop("context", None)

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

    return data


def _build_device_dict(manager: VeSync) -> dict:
    """Build a dictionary of ALL VeSync devices."""
    device_dict = {x.cid: x for x in manager.switches}
    device_dict.update({x.cid: x for x in manager.fans})
    device_dict.update({x.cid: x for x in manager.outlets})
    device_dict.update({x.cid: x for x in manager.bulbs})
    return device_dict


def _redact_device_values(device: VeSyncBaseDevice) -> dict:
    """Rebuild and redact values of a VeSync device."""
    data = {}
    for key, item in device.__dict__.items():
        if key not in KEYS_TO_REDACT:
            data[key] = item
        else:
            data[key] = REDACTED

    return data
