"""Log introspection — "where do my logs live, what's in effect, what
components log?" as queryable data.

:func:`describe_logging` answers, for the running process, the Layer-A
questions of the unified-logging design: the effective root level, the
active sinks + their targets (file path / stream / journald), the
per-component logger levels actually in effect, and the canonical sink
locations from the path helpers. It reads the **live** ``logging`` tree
+ the path helpers, so it reflects reality, not just configured intent.

Surfaced by ``mgf-common logs info`` (Wave 2) and the per-app web
``/logs/info`` endpoint (Wave 3). Design:
``docs/inprogress/UNIFIED_LOGGING_DESIGN.md``.
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field

from mgf.common.observability.log_record import LOG_SCHEMA_VERSION
from mgf.common.observability.paths import (
    default_crash_dir,
    default_log_path,
    default_state_dir,
)


@dataclass(frozen=True)
class HandlerInfo:
    """One active logging sink."""

    kind: str  # handler class name (e.g. RotatingFileHandler, StreamHandler)
    level: str  # level name gating this handler
    formatter: str | None  # formatter class name (JsonFormatter / ...)
    target: str | None  # file path, stream name, or None

    def to_dict(self) -> dict[str, object]:
        return {
            "kind": self.kind,
            "level": self.level,
            "formatter": self.formatter,
            "target": self.target,
        }


@dataclass(frozen=True)
class LogInfo:
    """The effective logging picture for a running app."""

    app: str
    app_version: str
    root_level: str
    root_level_no: int
    handlers: list[HandlerInfo] = field(default_factory=list)
    # Loggers with an explicit (non-NOTSET) level — the per-component
    # overrides actually in effect.
    component_levels: dict[str, str] = field(default_factory=dict)
    # Canonical sink locations (the "where do my logs live?" answer).
    locations: dict[str, str] = field(default_factory=dict)
    schema_version: str = LOG_SCHEMA_VERSION

    def to_dict(self) -> dict[str, object]:
        return {
            "app": self.app,
            "app_version": self.app_version,
            "root_level": self.root_level,
            "root_level_no": self.root_level_no,
            "handlers": [h.to_dict() for h in self.handlers],
            "component_levels": dict(self.component_levels),
            "locations": dict(self.locations),
            "schema_version": self.schema_version,
        }


def _handler_target(handler: logging.Handler) -> str | None:
    base = getattr(handler, "baseFilename", None)
    if base:
        return str(base)
    stream = getattr(handler, "stream", None)
    name = getattr(stream, "name", None)
    if name is not None:
        return str(name)
    socket_addr = getattr(handler, "address", None)
    if socket_addr is not None:
        return str(socket_addr)
    return None


def describe_logging() -> LogInfo:
    """Return the effective logging picture for the current process."""
    # Lazy import — observability is imported *during* mgf.common import,
    # so a top-level `from mgf.common import ...` would be circular.
    from mgf.common import app_name, app_version

    root = logging.getLogger()
    handlers: list[HandlerInfo] = []
    for h in root.handlers:
        fmt = type(h.formatter).__name__ if h.formatter is not None else None
        handlers.append(
            HandlerInfo(
                kind=type(h).__name__,
                level=logging.getLevelName(h.level),
                formatter=fmt,
                target=_handler_target(h),
            )
        )

    component_levels: dict[str, str] = {}
    for name, obj in sorted(logging.root.manager.loggerDict.items()):
        if isinstance(obj, logging.Logger) and obj.level != logging.NOTSET:
            component_levels[name] = logging.getLevelName(obj.level)

    locations = {
        "log_file": str(default_log_path()),
        "crash_dir": str(default_crash_dir()),
        "state_dir": str(default_state_dir()),
    }

    return LogInfo(
        app=app_name(),
        app_version=app_version(),
        root_level=logging.getLevelName(root.getEffectiveLevel()),
        root_level_no=root.getEffectiveLevel(),
        handlers=handlers,
        component_levels=component_levels,
        locations=locations,
    )


__all__ = ["HandlerInfo", "LogInfo", "describe_logging"]
