"""Rebrand opik's console logging output.

Upstream opik attaches a ``logging.StreamHandler`` to the ``"opik"`` logger
whose formatter prefixes every console line with ``OPIK: ``. Because the
``"opik"`` root logger has ``propagate=False`` and every ``opik.*`` child
logger bubbles up to that single root handler, swapping that one handler's
formatter rebrands all opik console output at once.

This module is the console-output analogue of the ``_*_patch`` modules: it
reaches into opik internals (the ``"opik"`` logger and ``opik._logging.setup``)
because there is no public extension point for log formatting.

Scope: console ``StreamHandler`` only. The optional DEBUG ``FileHandler`` and
the Sentry Event/Breadcrumb handlers are intentionally left untouched (not
console output). To extend to the file handler later, drop the
``not isinstance(h, logging.FileHandler)`` guard in ``_console_handlers``.
"""

import functools
import logging
import re

import opik._logging as _opik_logging

_PREFIX = "[acceldata_aio_tracer]"
_OPIK_LOGGER_NAME = "opik"

# Off-brand docs host. Sentences mentioning it are dropped wholesale so we
# never emit a dead link or the "comet" brand.
_OFFBRAND_HOST = "comet.com"
# Split on sentence boundaries (period/!/? followed by whitespace). URLs end
# in non-space, non-boundary chars, so a URL is never split mid-token.
_SENTENCE_SPLIT_RE = re.compile(r"(?<=[.!?])\s+")
# Whole-word brand token (not glued into an identifier like OpikUsage).
_BRAND_WORD_RE = re.compile(r"\bopik\b", re.IGNORECASE)
_MULTISPACE_RE = re.compile(r"  +")

# Module-level guard so ``setup`` is wrapped exactly once per process.
_setup_wrapped = False


def _scrub(text: str) -> str:
    """Remove off-brand sentences and rebrand standalone brand words."""
    sentences = _SENTENCE_SPLIT_RE.split(text)
    kept = [s for s in sentences if _OFFBRAND_HOST not in s.lower()]
    text = " ".join(kept).strip()
    text = _BRAND_WORD_RE.sub("AIO", text)
    text = _MULTISPACE_RE.sub(" ", text)
    return text


class _RebrandFormatter(_opik_logging.TruncateFormatter):
    """opik's TruncateFormatter with the brand rewritten in the prefix and
    scrubbed from the rendered message body."""

    def format(self, record: logging.LogRecord) -> str:
        return _scrub(super().format(record))


def _rebranded_fmt(existing: logging.Formatter | None) -> str:
    """Build the rebrand format string from opik's current format, replacing
    the literal ``OPIK`` (and an immediately-trailing colon) with our prefix.
    Falls back to opik's CONSOLE format if none is set. Preserves the DEBUG
    format's extra fields when console logging is in DEBUG mode."""
    base = getattr(existing, "_fmt", None) or _opik_logging.CONSOLE_MSG_FORMAT
    return re.sub(r"OPIK:?", _PREFIX, base)


def _console_handlers() -> list[logging.StreamHandler]:
    logger = logging.getLogger(_OPIK_LOGGER_NAME)
    return [
        h
        for h in logger.handlers
        if isinstance(h, logging.StreamHandler)
        and not isinstance(h, logging.FileHandler)
    ]


def _rebrand_handlers() -> None:
    """Swap the formatter on every opik console handler that isn't already
    rebranded. Idempotent."""
    for handler in _console_handlers():
        if isinstance(handler.formatter, _RebrandFormatter):
            continue
        handler.setFormatter(_RebrandFormatter(_rebranded_fmt(handler.formatter)))


def _wrap_setup() -> None:
    """Wrap ``opik._logging.setup`` so any later re-setup re-applies the
    rebrand. Wrapped at most once."""
    global _setup_wrapped
    if _setup_wrapped:
        return
    original = _opik_logging.setup

    @functools.wraps(original)
    def setup_then_rebrand(*args: object, **kwargs: object) -> object:
        result = original(*args, **kwargs)
        _rebrand_handlers()
        return result

    _opik_logging.setup = setup_then_rebrand
    _setup_wrapped = True


def apply() -> None:
    """Rebrand opik's console output. Idempotent — safe to call repeatedly."""
    _rebrand_handlers()
    _wrap_setup()
