"""Forward a curated allowlist of opik subpackages into the
``acceldata_aio_tracer`` namespace, so consumer code can import them as
``acceldata_aio_tracer.<sub>...`` without ever naming ``opik``.

This is the brand-neutral half of the white-label surface (the brand-carrying
integrations are handled by explicit alias re-exports in
``acceldata_aio_tracer.integrations``). It covers subtrees whose public symbol
names contain no "Opik" brand and which are too deep/large to mirror by hand
(the generated ``rest_api`` tree, the deep ``evaluation.metrics.llm_judges.*``
modules).

Mechanism: a ``sys.meta_path`` finder that, for any
``acceldata_aio_tracer.<sub>...`` whose first segment is allowlisted, imports
the matching ``opik.<sub>...`` module and registers that SAME module object
under the aio name. Aliasing the leaf object (not just the package node) is
what preserves identity — ``aio.evaluation.metrics.GEval is
opik.evaluation.metrics.GEval`` — so isinstance / pydantic checks that cross
the two namespaces keep working.

``integrations`` is deliberately NOT allowlisted: it is a real on-disk package
whose modules rebrand opik's ``Opik*`` names, and must win.
"""

from __future__ import annotations

import importlib
import importlib.abc
import importlib.machinery
import sys
from types import ModuleType
from typing import Sequence

_PACKAGE = "acceldata_aio_tracer"
_PREFIX = _PACKAGE + "."

# Allowlist of top-level opik subpackages exposed via the facade. Brand-neutral
# names only; excludes integrations (real on-disk package) and opik internals.
_FORWARDED_SUBTREES = frozenset({"evaluation", "rest_api", "opik_context"})

# Brand-neutral aliases: the aio-facing head segment -> the real opik subtree.
# Lets consumers import e.g. ``acceldata_aio_tracer.aio_context`` instead of the
# opik-branded ``opik_context`` (which stays available for compatibility).
_SUBTREE_ALIASES = {"aio_context": "opik_context"}


class _OpikForwardFinder(importlib.abc.MetaPathFinder, importlib.abc.Loader):
    """Resolve ``acceldata_aio_tracer.<allowlisted>...`` to the identical
    ``opik.<allowlisted>...`` module object."""

    def _opik_name(self, fullname: str) -> str | None:
        if not fullname.startswith(_PREFIX):
            return None
        sub = fullname[len(_PREFIX):]
        head, sep, rest = sub.partition(".")
        head = _SUBTREE_ALIASES.get(head, head)  # brand-neutral alias -> opik name
        if head not in _FORWARDED_SUBTREES:
            return None
        return "opik." + head + sep + rest

    def find_spec(
        self,
        fullname: str,
        path: Sequence[str] | None = None,
        target: ModuleType | None = None,
    ) -> importlib.machinery.ModuleSpec | None:
        if self._opik_name(fullname) is None:
            return None
        return importlib.machinery.ModuleSpec(fullname, self)

    def create_module(self, spec: importlib.machinery.ModuleSpec) -> ModuleType:
        opik_name = self._opik_name(spec.name)
        assert opik_name is not None  # find_spec already filtered
        module = importlib.import_module(opik_name)
        # importlib's module_from_spec() overwrites module.__spec__ with this
        # aio spec via _init_module_attrs() right after create_module returns.
        # Since `module` is the SHARED opik module, that would clobber
        # opik.<sub>.__spec__.name to the aio name (diverging from __name__ and
        # confusing tools that read __spec__). Stash the canonical opik spec on
        # `spec` so exec_module() — which runs afterwards — can restore it.
        spec._opik_original_spec = module.__spec__
        # Register the SAME object under the aio name so identity is preserved
        # and deeper submodule resolution reuses it.
        sys.modules[spec.name] = module
        return module

    def exec_module(self, module: ModuleType) -> None:
        # ``module`` was already executed as the opik module. Restore the
        # canonical opik __spec__ that module_from_spec() overwrote, so the
        # shared opik module keeps consistent __name__/__spec__ identity.
        original = getattr(module.__spec__, "_opik_original_spec", None)
        if original is not None:
            module.__spec__ = original


def install() -> None:
    """Install the forwarding finder on ``sys.meta_path``. Idempotent."""
    if any(isinstance(f, _OpikForwardFinder) for f in sys.meta_path):
        return
    sys.meta_path.insert(0, _OpikForwardFinder())
