"""WebSocket commands for saved filter views (v2.24).

``views/list`` returns the shared named views (read — any user applies them);
``views/save`` upserts one and ``views/delete`` removes one (write — creating or
deleting a shared view is a content change). Views live on the global config
entry's options; see ``helpers/saved_views`` for the shape + sanitiser.
"""

from __future__ import annotations

from typing import Any

import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant

from ..const import CONF_SAVED_FILTER_VIEWS, DOMAIN, MAX_ID_LENGTH, MAX_VIEW_NAME_LENGTH
from ..helpers.permissions import require_write
from ..helpers.saved_views import list_saved_views, remove_view, sanitize_view, upsert_view
from . import _get_global_entry


def _persist(hass: HomeAssistant, views: list[dict[str, Any]]) -> None:
    """Write the views list back to the global entry's options."""
    global_entry = _get_global_entry(hass)
    if global_entry is None:
        raise LookupError("global_entry_missing")
    options = dict(global_entry.options or global_entry.data)
    options[CONF_SAVED_FILTER_VIEWS] = views
    hass.config_entries.async_update_entry(global_entry, options=options)


@websocket_api.websocket_command({vol.Required("type"): f"{DOMAIN}/views/list"})
@websocket_api.async_response
async def ws_list_saved_views(
    hass: HomeAssistant,
    connection: websocket_api.ActiveConnection,
    msg: dict[str, Any],
) -> None:
    """Return every shared saved filter view."""
    connection.send_result(msg["id"], {"views": list_saved_views(hass)})


@websocket_api.websocket_command(
    {
        vol.Required("type"): f"{DOMAIN}/views/save",
        # Omit view_id to create; include it to update in place. (Not "id" — that
        # key is the WebSocket message id the framework owns.)
        vol.Optional("view_id"): vol.All(str, vol.Length(max=MAX_ID_LENGTH)),
        vol.Required("name"): vol.All(str, vol.Length(min=1, max=MAX_VIEW_NAME_LENGTH)),
        vol.Optional("filters"): dict,
    }
)
@require_write
@websocket_api.async_response
async def ws_save_saved_view(
    hass: HomeAssistant,
    connection: websocket_api.ActiveConnection,
    msg: dict[str, Any],
) -> None:
    """Create or update a shared saved view, then return the full list."""
    clean = sanitize_view(
        {"id": msg.get("view_id"), "name": msg["name"], "filters": msg.get("filters", {})},
        view_id=msg.get("view_id"),
    )
    if clean is None:
        connection.send_error(msg["id"], "invalid_view", "A view needs a non-empty name")
        return
    try:
        views, saved_id = upsert_view(list_saved_views(hass), clean)
        _persist(hass, views)
    except ValueError:
        connection.send_error(msg["id"], "too_many_views", "The saved-views limit has been reached")
        return
    except LookupError:
        connection.send_error(msg["id"], "not_found", "Global config entry not found")
        return
    connection.send_result(msg["id"], {"views": views, "saved_id": saved_id})


@websocket_api.websocket_command(
    {
        vol.Required("type"): f"{DOMAIN}/views/delete",
        vol.Required("view_id"): vol.All(str, vol.Length(min=1, max=MAX_ID_LENGTH)),
    }
)
@require_write
@websocket_api.async_response
async def ws_delete_saved_view(
    hass: HomeAssistant,
    connection: websocket_api.ActiveConnection,
    msg: dict[str, Any],
) -> None:
    """Delete a shared saved view by id, then return the remaining list."""
    views = remove_view(list_saved_views(hass), msg["view_id"])
    try:
        _persist(hass, views)
    except LookupError:
        connection.send_error(msg["id"], "not_found", "Global config entry not found")
        return
    connection.send_result(msg["id"], {"views": views})
