"""Prometheus exposition format formatter (zero dependencies)."""

from __future__ import annotations

_PRESSURE_MAP = {"normal": 0, "warn": 1, "critical": 2}


def _escape_label(value: str) -> str:
    """Escape label value for Prometheus format."""
    return value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")


def _gauge(name: str, help_text: str, value: float | int, labels: dict | None = None) -> str:
    """Format a single gauge metric with optional labels."""
    lines = [f"# HELP {name} {help_text}", f"# TYPE {name} gauge"]
    if labels:
        label_str = ",".join(f'{k}="{_escape_label(str(v))}"' for k, v in labels.items())
        lines.append(f"{name}{{{label_str}}} {value}")
    else:
        lines.append(f"{name} {value}")
    return "\n".join(lines)


def format_prometheus(snapshot: dict, benchmarks: list[dict] | None = None) -> str:
    """Convert a full snapshot dict to Prometheus exposition text format.

    Args:
        snapshot: Output from collect_full_snapshot().
        benchmarks: Optional latest benchmark results per engine+model.

    Returns:
        Prometheus text format string.
    """
    sections: list[str] = []

    # System metrics
    sections.append(
        _gauge("asiai_cpu_load_1m", "CPU load average 1 minute", snapshot.get("cpu_load_1", 0))
    )
    sections.append(
        _gauge("asiai_cpu_load_5m", "CPU load average 5 minutes", snapshot.get("cpu_load_5", 0))
    )
    sections.append(
        _gauge("asiai_cpu_load_15m", "CPU load average 15 minutes", snapshot.get("cpu_load_15", 0))
    )
    sections.append(
        _gauge("asiai_memory_used_bytes", "Memory used in bytes", snapshot.get("mem_used", 0))
    )
    sections.append(
        _gauge("asiai_memory_total_bytes", "Total memory in bytes", snapshot.get("mem_total", 0))
    )

    pressure = _PRESSURE_MAP.get(snapshot.get("mem_pressure", "normal"), 0)
    sections.append(
        _gauge(
            "asiai_memory_pressure_level",
            "Memory pressure level (0=normal 1=warn 2=critical)",
            pressure,
        )
    )

    speed_limit = snapshot.get("thermal_speed_limit", 100)
    sections.append(
        _gauge("asiai_thermal_speed_limit_pct", "CPU speed limit percentage", speed_limit)
    )

    # GPU metrics (conditional — only if ioreg data available)
    gpu_util = snapshot.get("gpu_utilization_pct", -1)
    if gpu_util >= 0:
        sections.append(
            _gauge("asiai_gpu_utilization_pct", "GPU device utilization percentage", gpu_util)
        )
        gpu_renderer = snapshot.get("gpu_renderer_pct", -1)
        if gpu_renderer >= 0:
            sections.append(
                _gauge(
                    "asiai_gpu_renderer_pct",
                    "GPU renderer utilization percentage",
                    gpu_renderer,
                )
            )
        gpu_tiler = snapshot.get("gpu_tiler_pct", -1)
        if gpu_tiler >= 0:
            sections.append(
                _gauge("asiai_gpu_tiler_pct", "GPU tiler utilization percentage", gpu_tiler)
            )
        sections.append(
            _gauge(
                "asiai_gpu_memory_in_use_bytes",
                "GPU system memory in use",
                snapshot.get("gpu_mem_in_use", 0),
            )
        )
        sections.append(
            _gauge(
                "asiai_gpu_memory_allocated_bytes",
                "GPU system memory allocated",
                snapshot.get("gpu_mem_allocated", 0),
            )
        )

    # Power metrics (IOReport, no sudo)
    power_gpu = snapshot.get("power_gpu_watts", -1)
    if power_gpu >= 0:
        sections.append(
            _gauge("asiai_power_gpu_watts", "GPU power consumption in watts", power_gpu)
        )
        sections.append(
            _gauge(
                "asiai_power_cpu_watts",
                "CPU power consumption in watts",
                snapshot.get("power_cpu_watts", 0),
            )
        )
        sections.append(
            _gauge(
                "asiai_power_ane_watts",
                "ANE power consumption in watts",
                snapshot.get("power_ane_watts", 0),
            )
        )
        sections.append(
            _gauge(
                "asiai_power_total_watts",
                "Total package power consumption in watts",
                snapshot.get("power_total_watts", 0),
            )
        )

    # Per-engine metrics
    engines_status = snapshot.get("engines_status", [])
    if engines_status:
        engine_lines: list[str] = [
            "# HELP asiai_engine_reachable Whether the engine is reachable (0 or 1)",
            "# TYPE asiai_engine_reachable gauge",
        ]
        models_loaded_lines: list[str] = [
            "# HELP asiai_engine_models_loaded Number of models loaded",
            "# TYPE asiai_engine_models_loaded gauge",
        ]
        vram_lines: list[str] = [
            "# HELP asiai_engine_vram_bytes Total VRAM used by engine",
            "# TYPE asiai_engine_vram_bytes gauge",
        ]
        info_lines: list[str] = [
            "# HELP asiai_engine_version_info Engine version information",
            "# TYPE asiai_engine_version_info gauge",
        ]

        tcp_lines: list[str] = [
            "# HELP asiai_engine_tcp_connections Established TCP connections to engine",
            "# TYPE asiai_engine_tcp_connections gauge",
        ]
        req_lines: list[str] = [
            "# HELP asiai_engine_requests_processing Requests currently being processed",
            "# TYPE asiai_engine_requests_processing gauge",
        ]
        kv_lines: list[str] = [
            "# HELP asiai_engine_kv_cache_usage_ratio KV cache usage ratio",
            "# TYPE asiai_engine_kv_cache_usage_ratio gauge",
        ]
        tok_lines: list[str] = [
            "# HELP asiai_engine_tokens_predicted_total Cumulative tokens predicted",
            "# TYPE asiai_engine_tokens_predicted_total counter",
        ]

        for es in engines_status:
            name = _escape_label(es["name"])
            reachable = 1 if es.get("reachable") else 0
            engine_lines.append(f'asiai_engine_reachable{{engine="{name}"}} {reachable}')
            models_loaded_lines.append(
                f'asiai_engine_models_loaded{{engine="{name}"}} {len(es.get("models", []))}'
            )
            vram_lines.append(
                f'asiai_engine_vram_bytes{{engine="{name}"}} {es.get("vram_total", 0)}'
            )
            version = _escape_label(es.get("version", ""))
            info_lines.append(f'asiai_engine_version_info{{engine="{name}",version="{version}"}} 1')

            # Inference activity gauges
            tcp = es.get("tcp_connections", 0)
            tcp_lines.append(f'asiai_engine_tcp_connections{{engine="{name}"}} {tcp}')
            req = es.get("requests_processing", 0)
            req_lines.append(f'asiai_engine_requests_processing{{engine="{name}"}} {req}')
            kv = es.get("kv_cache_usage_ratio", -1)
            if kv >= 0:
                kv_lines.append(f'asiai_engine_kv_cache_usage_ratio{{engine="{name}"}} {kv}')
            tok_total = es.get("tokens_predicted_total", 0)
            if tok_total > 0:
                tok_lines.append(
                    f'asiai_engine_tokens_predicted_total{{engine="{name}"}} {tok_total}'
                )

        sections.append("\n".join(engine_lines))
        sections.append("\n".join(models_loaded_lines))
        sections.append("\n".join(vram_lines))
        sections.append("\n".join(info_lines))
        sections.append("\n".join(tcp_lines))
        sections.append("\n".join(req_lines))
        if len(kv_lines) > 2:
            sections.append("\n".join(kv_lines))
        if len(tok_lines) > 2:
            sections.append("\n".join(tok_lines))

        # Per-model metrics
        model_vram: list[str] = [
            "# HELP asiai_model_vram_bytes VRAM used by model",
            "# TYPE asiai_model_vram_bytes gauge",
        ]
        model_ctx: list[str] = [
            "# HELP asiai_model_context_length Model context window size",
            "# TYPE asiai_model_context_length gauge",
        ]
        model_loaded: list[str] = [
            "# HELP asiai_model_loaded Whether the model is loaded (1=yes)",
            "# TYPE asiai_model_loaded gauge",
        ]

        for es in engines_status:
            ename = _escape_label(es["name"])
            for m in es.get("models", []):
                mname = _escape_label(m["name"])
                lbl = f'engine="{ename}",model="{mname}"'
                vram = m.get("size_vram", 0)
                model_vram.append(f"asiai_model_vram_bytes{{{lbl}}} {vram}")
                ctx = m.get("context_length", 0)
                model_ctx.append(f"asiai_model_context_length{{{lbl}}} {ctx}")
                model_loaded.append(f'asiai_model_loaded{{engine="{ename}",model="{mname}"}} 1')

        if any(len(lines) > 2 for lines in [model_vram, model_ctx, model_loaded]):
            sections.append("\n".join(model_vram))
            sections.append("\n".join(model_ctx))
            sections.append("\n".join(model_loaded))

    # Benchmark metrics (optional)
    if benchmarks:
        tok_lines = [
            "# HELP asiai_bench_tok_per_sec Last benchmark tokens per second",
            "# TYPE asiai_bench_tok_per_sec gauge",
        ]
        ttft_lines = [
            "# HELP asiai_bench_ttft_seconds Last benchmark time to first token",
            "# TYPE asiai_bench_ttft_seconds gauge",
        ]
        ttft_client_lines = [
            "# HELP asiai_bench_ttft_client_seconds Last benchmark client-side time to first token",
            "# TYPE asiai_bench_ttft_client_seconds gauge",
        ]
        power_lines = [
            "# HELP asiai_bench_power_watts Last benchmark power consumption",
            "# TYPE asiai_bench_power_watts gauge",
        ]

        for b in benchmarks:
            ename = _escape_label(b.get("engine", ""))
            mname = _escape_label(b.get("model", ""))
            labels = f'engine="{ename}",model="{mname}"'
            tok_per_sec = b.get("tok_per_sec", 0)
            if tok_per_sec:
                tok_lines.append(f"asiai_bench_tok_per_sec{{{labels}}} {tok_per_sec}")
            ttft = b.get("ttft_ms", 0)
            if ttft:
                ttft_lines.append(f"asiai_bench_ttft_seconds{{{labels}}} {ttft / 1000.0}")
            ttft_client = b.get("ttft_client_ms", 0)
            if ttft_client:
                ttft_client_lines.append(
                    f"asiai_bench_ttft_client_seconds{{{labels}}} {ttft_client / 1000.0}"
                )
            power = b.get("power_watts", 0)
            if power:
                power_lines.append(f"asiai_bench_power_watts{{{labels}}} {power}")

        if len(tok_lines) > 2:
            sections.append("\n".join(tok_lines))
        if len(ttft_lines) > 2:
            sections.append("\n".join(ttft_lines))
        if len(ttft_client_lines) > 2:
            sections.append("\n".join(ttft_client_lines))
        if len(power_lines) > 2:
            sections.append("\n".join(power_lines))

    return "\n\n".join(sections) + "\n"
