"""mgf-fed logging bootstrap — ship the CLI's logs to the federation log host.

Logging L2, item (e). When the central collector is configured via the
environment, attach mgf-logship's :class:`~mgf.logship.LogShipper` to the root
logger so every ``mgf-fed`` invocation's log records reach openobserve-prod
(``app="mgf-fed"``). The collector forwards them to OpenObserve over the proven
NDJSON ``/ingest`` path (bearer-token-gated).

Design constraints:

* mgf-fed's orchestration core is **zero-dependency** — ``mgf-logship`` is only
  present with the ``[cli]`` extra. So the import is lazy and guarded: no
  collector configured, or ``mgf-logship`` not installed, ⇒ a silent no-op. The
  CLI runs identically either way.
* Logging setup must **never** break the CLI. Every failure path degrades to
  silence rather than raising into ``main()``.
* :class:`~mgf.logship.LogShipper` is a fail-open, bounded, background-thread
  handler whose ``close()`` performs a final synchronous flush. Python's
  ``logging.shutdown`` (registered at interpreter exit) calls it, so records
  queued by a short-lived CLI invocation are flushed before exit — no extra
  ``atexit`` wiring is needed here.

Environment contract (app side):

==============================  ==================================================
``MGF_LOGSHIP_URL``             collector base URL, e.g. ``https://<host>.sslip.io``
                                (the shipper appends ``/ingest`` itself)
``MGF_LOGSHIP_INGEST_TOKEN``    inbound bearer token the collector gates on
``MGF_LOGSHIP_ENV``             environment tag on each record (default ``prod``)
``MGF_LOG_LEVEL``               root level so records actually flow (default ``INFO``)
==============================  ==================================================

Both ``MGF_LOGSHIP_URL`` and ``MGF_LOGSHIP_INGEST_TOKEN`` must be set to enable
shipping; either one missing leaves logging untouched.
"""

from __future__ import annotations

import atexit
import contextlib
import logging
import os
import time
from collections.abc import Iterator

_APP = "mgf-fed"
_DEFAULT_ENV = "prod"
_DEFAULT_LEVEL = "INFO"

# Module logger for the orchestration core. Records ship to the central log host
# when bootstrap_logging() attaches the shipper; otherwise they fall through
# stdlib logging like any other record (INFO stays quiet, ERROR reaches
# logging.lastResort/stderr) — keeping the zero-dep core honest about failures.
LOG = logging.getLogger("mgf.fed")


@contextlib.contextmanager
def log_operation(operation: str, **fields: object) -> Iterator[None]:
    """Bracket a fleet operation with INFO start/end records (LG-11).

    Emits an INFO ``<operation>.start`` record, then an INFO ``<operation>.end``
    record carrying a stable ``operation`` field plus ``duration_ms``. On failure
    it emits an ERROR record with ``exc_info=True`` (LG-05) — so the traceback
    reaches the central log instead of vanishing — and re-raises unchanged.

    ``operation`` is the stable, low-cardinality verb (e.g. ``release.remote``);
    put high-cardinality detail (repo names, counts) in ``fields``.
    """
    start = time.monotonic()
    LOG.info(
        "%s start",
        operation,
        extra={"operation": operation, "event": f"{operation}.start", **fields},
    )
    try:
        yield
    except Exception:
        duration_ms = round((time.monotonic() - start) * 1000, 1)
        LOG.error(
            "%s failed",
            operation,
            exc_info=True,
            extra={
                "operation": operation,
                "event": f"{operation}.error",
                "duration_ms": duration_ms,
                **fields,
            },
        )
        raise
    duration_ms = round((time.monotonic() - start) * 1000, 1)
    LOG.info(
        "%s ok",
        operation,
        extra={
            "operation": operation,
            "event": f"{operation}.end",
            "duration_ms": duration_ms,
            **fields,
        },
    )


def bootstrap_logging() -> bool:
    """Attach the central log shipper when the environment configures it.

    Returns ``True`` if a shipper was attached, ``False`` otherwise (not
    configured, ``mgf-logship`` absent, or already attached). Always safe to
    call; never raises.
    """
    endpoint = os.environ.get("MGF_LOGSHIP_URL")
    token = os.environ.get("MGF_LOGSHIP_INGEST_TOKEN")
    if not endpoint or not token:
        return False  # central logging not configured → leave logging untouched

    try:
        from mgf.logship import LogShipper
    except ImportError:
        return False  # mgf-logship not installed (zero-dep core) → nothing to ship with

    root = logging.getLogger()
    # Idempotent: don't double-attach if main() is re-entered (tests, embedding).
    if any(isinstance(h, LogShipper) for h in root.handlers):
        return False

    _establish_app_context()

    try:
        shipper = LogShipper(
            endpoint,
            token=token,
            app=_APP,
            env=os.environ.get("MGF_LOGSHIP_ENV", _DEFAULT_ENV),
        )
    except Exception:  # construction must never crash the CLI
        return False

    root.addHandler(shipper)
    # The root logger defaults to WARNING; mgf-fed emits at INFO, so without
    # this those records never reach the shipper. Only ever LOWER the threshold,
    # so an operator who explicitly quietened the root isn't overridden louder.
    level = getattr(logging, os.environ.get("MGF_LOG_LEVEL", _DEFAULT_LEVEL).upper(), logging.INFO)
    if root.level == logging.NOTSET or root.level > level:
        root.setLevel(level)
    return True


def _establish_app_context() -> None:
    """Run mgf-common's canonical bootstrap so shipped records carry app/trace
    context and mgf-common stops warning that ``current_context()`` ran before
    bootstrap. The shipper enriches each record via ``mgf.common.app_version()``,
    which is unset (and warns) outside an app context.

    Best-effort and guarded: mgf-common is only present with the ``[cli]`` extra,
    and a bootstrap failure must not stop log shipping. The returned context's
    ``shutdown`` is registered at exit to flush/clean up. Called only after the
    LogShipper import succeeds, so it never fires on the zero-dep base path.
    """
    try:
        from mgf.common import bootstrap

        from mgf.fed import __version__
    except ImportError:
        return
    try:
        ctx = bootstrap(app_name=_APP, app_version=__version__)
    except Exception:  # context setup must never crash the CLI
        return
    shutdown = getattr(ctx, "shutdown", None)
    if callable(shutdown):
        atexit.register(shutdown)
