"""AnyWidget base class for custom Jupyter widgets."""

from __future__ import annotations

from typing import Any

import ipywidgets
import traitlets.traitlets as t

from ._file_contents import FileContents, VirtualFileContents
from ._util import (
    _ANYWIDGET_ID_KEY,
    _CSS_KEY,
    _DEFAULT_ESM,
    _ESM_KEY,
    enable_custom_widget_manager_once,
    in_colab,
    repr_mimebundle,
    try_file_contents,
)
from ._version import _ANYWIDGET_SEMVER_VERSION
from .experimental import _collect_anywidget_commands, _register_anywidget_commands


class AnyWidget(ipywidgets.DOMWidget):  # type: ignore [misc]
    """Main AnyWidget base class."""

    _model_name = t.Unicode("AnyModel").tag(sync=True)
    _model_module = t.Unicode("anywidget").tag(sync=True)
    _model_module_version = t.Unicode(_ANYWIDGET_SEMVER_VERSION).tag(sync=True)

    _view_name = t.Unicode("AnyView").tag(sync=True)
    _view_module = t.Unicode("anywidget").tag(sync=True)
    _view_module_version = t.Unicode(_ANYWIDGET_SEMVER_VERSION).tag(sync=True)

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        if in_colab():
            enable_custom_widget_manager_once()

        anywidget_traits = {}
        for key in (_ESM_KEY, _CSS_KEY):
            if hasattr(self, key) and not self.has_trait(key):
                value = getattr(self, key)
                anywidget_traits[key] = t.Unicode(str(value)).tag(sync=True)
                if isinstance(value, (VirtualFileContents, FileContents)):
                    value.changed.connect(
                        lambda new_contents, key=key: setattr(self, key, new_contents)
                    )

        # show default _esm if not defined
        if not hasattr(self, _ESM_KEY):
            anywidget_traits[_ESM_KEY] = t.Unicode(_DEFAULT_ESM).tag(sync=True)

        # TODO: a better way to uniquely identify this subclasses?
        # We use the fully-qualified name to get an id which we
        # can use to update CSS if necessary.
        anywidget_traits[_ANYWIDGET_ID_KEY] = t.Unicode(
            f"{self.__class__.__module__}.{self.__class__.__name__}"
        ).tag(sync=True)

        self.add_traits(**anywidget_traits)
        super().__init__(*args, **kwargs)
        _register_anywidget_commands(self)

    def __init_subclass__(cls, **kwargs: dict) -> None:
        """Coerces _esm and _css to FileContents if they are files."""
        super().__init_subclass__(**kwargs)
        for key in (_ESM_KEY, _CSS_KEY) & cls.__dict__.keys():
            # TODO: Upgrate to := when we drop Python 3.7
            file_contents = try_file_contents(getattr(cls, key))
            if file_contents:
                setattr(cls, key, file_contents)
        _collect_anywidget_commands(cls)

    def _repr_mimebundle_(self, **kwargs: dict) -> tuple[dict, dict] | None:
        plaintext = repr(self)
        if len(plaintext) > 110:
            plaintext = plaintext[:110] + "…"
        if self._view_name is None:
            return None  # type: ignore[unreachable]
        return repr_mimebundle(model_id=self.model_id, repr_text=plaintext)
