"""Helpers for rewriting entity_id references when HA renames an entity.

Used by the global ``EVENT_ENTITY_REGISTRY_UPDATED`` listener in ``__init__.py``
to keep ``trigger_config["entity_id"|"entity_ids"]`` and
``adaptive_config["environmental_entity"]`` in sync with the user's renames.

Without this, ``async_track_state_change_event`` (which subscribes by literal
entity_id) silently misses events on the new id — same dual-storage class of
bug as #48, but with feature breakage instead of a UI inconsistency.
"""

from __future__ import annotations

from typing import Any


def rewrite_trigger_config(config: dict[str, Any], old_id: str, new_id: str) -> tuple[dict[str, Any], bool]:
    """Return (new_config, changed) with all entity_id references rewritten.

    Handles:
      - flat ``entity_id`` (legacy single-entity)
      - ``entity_ids`` list (multi-entity)
      - ``_trigger_state`` keyed by entity_id (per-entity persisted state)
      - compound triggers — recursive into ``conditions[].trigger_config``
    """
    if not isinstance(config, dict):
        return config, False

    new_config = dict(config)
    changed = False

    if new_config.get("entity_id") == old_id:
        new_config["entity_id"] = new_id
        changed = True

    eids = new_config.get("entity_ids")
    if isinstance(eids, list) and old_id in eids:
        new_config["entity_ids"] = [new_id if e == old_id else e for e in eids]
        changed = True

    state = new_config.get("_trigger_state")
    if isinstance(state, dict) and old_id in state:
        new_state = dict(state)
        new_state[new_id] = new_state.pop(old_id)
        new_config["_trigger_state"] = new_state
        changed = True

    if new_config.get("type") == "compound":
        new_conditions: list[dict[str, Any]] = []
        for cond in new_config.get("conditions", []) or []:
            new_cond, cond_changed = rewrite_trigger_config(cond, old_id, new_id)
            inner = new_cond.get("trigger_config") if isinstance(new_cond, dict) else None
            if isinstance(inner, dict):
                rewritten_inner, inner_changed = rewrite_trigger_config(inner, old_id, new_id)
                if inner_changed:
                    new_cond = {**new_cond, "trigger_config": rewritten_inner}
                    cond_changed = True
            if cond_changed:
                changed = True
            new_conditions.append(new_cond)
        if changed:
            new_config["conditions"] = new_conditions

    return new_config, changed


def rewrite_task(task_data: dict[str, Any], old_id: str, new_id: str) -> tuple[dict[str, Any], bool]:
    """Rewrite trigger_config + adaptive_config.environmental_entity in a task.

    Note: ``adaptive_config`` lives in the Store after the v1.x migration —
    this helper still rewrites the inline copy for the legacy / pre-migration
    path. The Store path is handled by ``rewrite_store``.
    """
    new_task = dict(task_data)
    changed = False

    tc = new_task.get("trigger_config")
    if isinstance(tc, dict):
        new_tc, tc_changed = rewrite_trigger_config(tc, old_id, new_id)
        if tc_changed:
            new_task["trigger_config"] = new_tc
            changed = True

    ac = new_task.get("adaptive_config")
    if isinstance(ac, dict) and ac.get("environmental_entity") == old_id:
        new_task["adaptive_config"] = {**ac, "environmental_entity": new_id}
        changed = True

    return new_task, changed


def rewrite_tasks(tasks: dict[str, dict[str, Any]], old_id: str, new_id: str) -> tuple[dict[str, dict[str, Any]], bool]:
    """Rewrite all tasks in an entry; return (new_tasks, any_changed)."""
    new_tasks: dict[str, dict[str, Any]] = {}
    any_changed = False
    for tid, td in tasks.items():
        new_td, changed = rewrite_task(td, old_id, new_id)
        new_tasks[tid] = new_td
        if changed:
            any_changed = True
    return new_tasks, any_changed


def rewrite_store(store: Any, old_id: str, new_id: str) -> bool:
    """Rewrite ``adaptive_config.environmental_entity`` and ``trigger_runtime``
    keys in a ``MaintenanceStore``.

    These fields live in Store (not entry.data) after the v1.x migration —
    `_DYNAMIC_TASK_FIELDS` includes ``adaptive_config``, and per-entity
    trigger runtime is keyed by entity_id. Both must follow renames.

    Returns True iff anything was rewritten.
    """
    changed = False
    tasks_state: dict[str, Any] = store._data.get("tasks", {})
    for state in tasks_state.values():
        ac = state.get("adaptive_config")
        if isinstance(ac, dict) and ac.get("environmental_entity") == old_id:
            state["adaptive_config"] = {**ac, "environmental_entity": new_id}
            changed = True
        runtime = state.get("trigger_runtime")
        if isinstance(runtime, dict) and old_id in runtime:
            runtime[new_id] = runtime.pop(old_id)
            changed = True
    return changed


def migrate_object_unique_ids(hass: Any, entry: Any, old_name: str | None, new_name: str | None) -> int:
    """Rewrite per-task entity unique_ids after an object RENAME.

    Same dual-storage bug class as the trigger rewrites above, but for our
    own entities: every per-task unique_id embeds the object's NAME SLUG
    (``maintenance_supporter_{slug}_{task_id}[_suffix]``). Without this
    migration a rename orphans all registry entries on the next reload —
    the entities come back under NEW unique_ids (and new entity_ids), while
    dashboards and automations keep pointing at the now-unavailable old ones.

    Rewrites in place, preserving entity_ids, history, and user
    customisations. Returns the number of migrated entries. A slug collision
    (another object already using the new unique_id) is logged and skipped
    rather than raised — the reload then falls back to fresh entities for
    just that entry, which matches the pre-migration behaviour.
    """
    from homeassistant.helpers import entity_registry as er

    from ..const import slugify_object_name

    old_slug = slugify_object_name(old_name or "")
    new_slug = slugify_object_name(new_name or "")
    if not old_slug or not new_slug or old_slug == new_slug:
        return 0

    old_prefix = f"maintenance_supporter_{old_slug}_"
    new_prefix = f"maintenance_supporter_{new_slug}_"
    ent_reg = er.async_get(hass)
    migrated = 0
    for reg_entry in er.async_entries_for_config_entry(ent_reg, entry.entry_id):
        uid = reg_entry.unique_id or ""
        if not uid.startswith(old_prefix):
            continue
        new_uid = new_prefix + uid[len(old_prefix) :]
        try:
            ent_reg.async_update_entity(reg_entry.entity_id, new_unique_id=new_uid)
            migrated += 1
        except ValueError:
            import logging

            logging.getLogger(__name__).warning(
                "unique_id %s already taken while renaming %r -> %r; leaving %s",
                new_uid,
                old_name,
                new_name,
                reg_entry.entity_id,
            )
    return migrated
