from __future__ import annotations

import logging
import sys
from statistics import mean
from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    from relationalai.config.config import Config

EXECUTION_LOGGER_NAME = "relationalai.client.execution"


class BufferedExecutionLogHandler(logging.Handler):
    def __init__(self) -> None:
        super().__init__(level=logging.INFO)
        self.lines: list[str] = []

    def emit(self, record: logging.LogRecord) -> None:
        try:
            msg = self.format(record)
        except Exception:
            # Keep diagnostics best-effort; avoid breaking CLI execution.
            msg = record.getMessage()
        self.lines.append(msg)


def enable_execution_logging_buffer(cfg: "Config") -> BufferedExecutionLogHandler | None:
    """Buffer execution middleware logs when enabled in config.

    This avoids interleaving logs with spinners and tables. Buffered logs should be
    flushed at the end of the CLI command.
    """
    if not cfg.execution.logging:
        return None

    log = logging.getLogger(EXECUTION_LOGGER_NAME)
    level = logging.INFO
    log.setLevel(level)
    log.propagate = False

    existing = next((h for h in log.handlers if isinstance(h, BufferedExecutionLogHandler)), None)
    if existing is not None:
        return existing

    h = BufferedExecutionLogHandler()
    h.setLevel(level)
    h.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s"))
    log.addHandler(h)
    return h


def flush_execution_logging_buffer(handler: BufferedExecutionLogHandler | None) -> None:
    if handler is None or not handler.lines:
        return
    sys.stderr.write(
        "\n"
        + "Execution logs\n"
        + "--------------\n"
        + "\n".join(handler.lines).rstrip()
        + "\n"
    )
    handler.lines.clear()


def disable_execution_logging_buffer(handler: BufferedExecutionLogHandler | None) -> None:
    if handler is None:
        return
    log = logging.getLogger(EXECUTION_LOGGER_NAME)
    try:
        log.removeHandler(handler)
    except Exception:
        pass


def _format_execution_metrics(metrics: Any | None) -> str | None:
    if metrics is None:
        return None

    counters: dict[str, Any] | None = getattr(metrics, "counters", None)
    timings: dict[str, Any] | None = getattr(metrics, "timings_ms", None)

    has_counters = isinstance(counters, dict) and any(v for v in counters.values())
    has_timings = isinstance(timings, dict) and any(bool(v) for v in timings.values())
    if not has_counters and not has_timings:
        return None

    lines: list[str] = []
    lines.append("")
    lines.append("Execution metrics")
    lines.append("-----------------")

    if has_counters and counters is not None:
        lines.append("Counters:")
        for k in sorted(counters.keys()):
            v = counters.get(k, 0)
            if v:
                lines.append(f"- {k}: {v}")

    if has_timings and timings is not None:
        lines.append("")
        lines.append("Timings (ms):")
        for k in sorted(timings.keys()):
            xs = timings.get(k) or []
            if not xs:
                continue
            xs_sorted = sorted(float(x) for x in xs)
            n = len(xs_sorted)
            p50 = xs_sorted[int(0.50 * (n - 1))]
            p95 = xs_sorted[int(0.95 * (n - 1))]
            lines.append(
                f"- {k}: n={n} min={xs_sorted[0]:.2f} avg={mean(xs_sorted):.2f} "
                f"p50={p50:.2f} p95={p95:.2f} max={xs_sorted[-1]:.2f}"
            )

    return "\n".join(lines)


def print_execution_metrics_if_enabled(cfg: "Config", metrics: Any | None) -> None:
    if not cfg.execution.metrics:
        return
    out = _format_execution_metrics(metrics)
    if out is None:
        return
    sys.stdout.write(out.rstrip() + "\n")

