"""gr.BrowserState() component."""

from __future__ import annotations

import secrets
import string
from typing import Any

from gradio_client.documentation import document
from pydantic import BaseModel as PydanticBaseModel

from gradio.components.base import Component
from gradio.events import Events


def _to_json_serializable(value: Any) -> Any:
    """Convert a value to a JSON-serializable form.

    Pydantic BaseModel instances are converted to dicts via model_dump(),
    since they cannot be directly serialized by orjson and would otherwise
    fall back to str() representation.
    """
    if isinstance(value, PydanticBaseModel):
        return value.model_dump()
    return value


@document()
class BrowserState(Component):
    EVENTS = [Events.change]
    """
    Special component that stores state in the browser's localStorage in an encrypted format.
    """

    def __init__(
        self,
        default_value: Any = None,
        *,
        storage_key: str | None = None,
        secret: str | None = None,
        render: bool = True,
    ):
        """
        Parameters:
            default_value: the default value that will be used if no value is found in localStorage. Should be a json-serializable value.
            storage_key: the key to use in localStorage. If None, a random key will be generated.
            secret: the secret key to use for encryption. If None, a random key will be generated (recommended).
            render: should always be True, is included for consistency with other components.
        """
        self.default_value = _to_json_serializable(default_value)
        self.secret = secret or "".join(
            secrets.choice(string.ascii_letters + string.digits) for _ in range(16)
        )
        self.storage_key = storage_key or "".join(
            secrets.choice(string.ascii_letters + string.digits) for _ in range(16)
        )
        self._value_description = "any json-serializable value"

        super().__init__(render=render)

    def preprocess(self, payload: Any) -> Any:
        """
        Parameters:
            payload: Value from local storage
        Returns:
            Passes value through unchanged
        """
        if payload is None:
            return self.default_value
        return payload

    def postprocess(self, value: Any) -> Any:
        """
        Parameters:
            value: Value to store in local storage
        Returns:
            Passes value through unchanged, converting Pydantic models to dicts
        """
        return _to_json_serializable(value)

    def api_info(self) -> dict[str, Any]:
        return {"type": {}, "description": "any json-serializable value"}

    def example_payload(self) -> Any:
        return "test"

    def example_value(self) -> Any:
        return "test"

    def breaks_grouping(self) -> bool:
        """BrowserState components should not break wrapper grouping chains."""
        return False
