"""Endpoint /metrics en formato texto Prometheus (exposition format v0.0.4).

Implementado sin `prometheus-client` para no añadir dependencias obligatorias.
El formato es compatible con cualquier scraper Prometheus estándar.

Métricas expuestas:
  supervisor_proposals_total{status}         — propuestas por estado (BD)
  supervisor_automation_runs_total{status}   — runs por estado (BD)
  supervisor_studies_total                   — estudios creados (BD)
  supervisor_memories_total                  — entradas de memoria aprobadas (BD)
  supervisor_inbound_events_total            — eventos recibidos del portal (BD)
  supervisor_pending_proposals               — gauge: pendientes ahora (BD)
  supervisor_scheduler_running               — gauge: 0/1 scheduler activo
  supervisor_automation_rules_enabled        — gauge: reglas habilitadas (BD)
  supervisor_agent_chat_total{kind}          — in-process counter por agente
  supervisor_commands_total                  — in-process counter

Los contadores in-process se resetean al reiniciar el proceso.
Los contadores de BD son consultas en tiempo real (pull model).
"""
from __future__ import annotations

import threading
import time
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from .scheduler import AutomationScheduler
    from .storage import SupervisorStore

# ------------------------------------------------------------------ #
# Contadores in-process (no requieren BD)                              #
# ------------------------------------------------------------------ #

_lock = threading.Lock()
_counters: dict[str, int] = {}


def inc(name: str, labels: dict[str, str] | None = None) -> None:
    """Incrementa un contador in-process de forma thread-safe."""
    key = _make_key(name, labels or {})
    with _lock:
        _counters[key] = _counters.get(key, 0) + 1


def _make_key(name: str, labels: dict[str, str]) -> str:
    if not labels:
        return name
    label_str = ",".join(f'{k}="{v}"' for k, v in sorted(labels.items()))
    return f"{name}{{{label_str}}}"


def _counter_line(name: str, labels: dict[str, str], value: int) -> str:
    key = _make_key(name, labels)
    return f"{key} {value}"


# ------------------------------------------------------------------ #
# Generación del exposition text                                       #
# ------------------------------------------------------------------ #

def generate_metrics(store: "SupervisorStore", scheduler: "AutomationScheduler") -> str:
    """Genera la respuesta completa en Prometheus exposition format."""
    lines: list[str] = []
    ts = int(time.time() * 1000)  # opcional: timestamp ms

    # ---- métricas de BD ----
    try:
        counts = store.count_summary()
    except Exception:
        counts = {}

    try:
        proposals_by_status = _count_proposals_by_status(store)
    except Exception:
        proposals_by_status = {}

    try:
        runs_by_status = _count_runs_by_status(store)
    except Exception:
        runs_by_status = {}

    try:
        inbound_total = _count_inbound(store)
    except Exception:
        inbound_total = 0

    # ---- scheduler state ----
    sched_status = scheduler.get_status()
    sched_running = 1 if sched_status.get("running") else 0

    # ---- in-process counters snapshot ----
    with _lock:
        inprocess = dict(_counters)

    # --- supervisor_proposals_total ---
    lines.append("# HELP supervisor_proposals_total Propuestas creadas por estado")
    lines.append("# TYPE supervisor_proposals_total counter")
    for status, val in proposals_by_status.items():
        lines.append(f'supervisor_proposals_total{{status="{status}"}} {val}')

    # --- supervisor_automation_runs_total ---
    lines.append("# HELP supervisor_automation_runs_total Ejecuciones de automatización por estado")
    lines.append("# TYPE supervisor_automation_runs_total counter")
    for status, val in runs_by_status.items():
        lines.append(f'supervisor_automation_runs_total{{status="{status}"}} {val}')

    # --- supervisor_studies_total ---
    lines.append("# HELP supervisor_studies_total Estudios creados en total")
    lines.append("# TYPE supervisor_studies_total counter")
    lines.append(f'supervisor_studies_total {counts.get("studies", 0)}')

    # --- supervisor_memories_total ---
    lines.append("# HELP supervisor_memories_total Entradas en memoria aprobadas")
    lines.append("# TYPE supervisor_memories_total counter")
    lines.append(f'supervisor_memories_total {counts.get("memories", 0)}')

    # --- supervisor_inbound_events_total ---
    lines.append("# HELP supervisor_inbound_events_total Eventos recibidos del portal PHP")
    lines.append("# TYPE supervisor_inbound_events_total counter")
    lines.append(f'supervisor_inbound_events_total {inbound_total}')

    # --- supervisor_pending_proposals (gauge) ---
    lines.append("# HELP supervisor_pending_proposals Propuestas pendientes de aprobación")
    lines.append("# TYPE supervisor_pending_proposals gauge")
    lines.append(f'supervisor_pending_proposals {counts.get("pending", 0)}')

    # --- supervisor_scheduler_running (gauge) ---
    lines.append("# HELP supervisor_scheduler_running 1 si el scheduler está activo")
    lines.append("# TYPE supervisor_scheduler_running gauge")
    lines.append(f'supervisor_scheduler_running {sched_running}')

    # --- supervisor_automation_rules_enabled (gauge) ---
    lines.append("# HELP supervisor_automation_rules_enabled Reglas de automatización habilitadas")
    lines.append("# TYPE supervisor_automation_rules_enabled gauge")
    lines.append(f'supervisor_automation_rules_enabled {counts.get("automation_enabled", 0)}')

    # --- supervisor_automation_runs_total_db (total desde BD) ---
    lines.append("# HELP supervisor_automation_runs_db_total Total de runs en BD")
    lines.append("# TYPE supervisor_automation_runs_db_total counter")
    lines.append(f'supervisor_automation_runs_db_total {counts.get("automation_runs", 0)}')

    # --- in-process: agent chats ---
    lines.append("# HELP supervisor_agent_chat_total Consultas directas a agente por tipo")
    lines.append("# TYPE supervisor_agent_chat_total counter")
    any_agent = False
    for key, val in inprocess.items():
        if key.startswith("supervisor_agent_chat_total"):
            lines.append(f"{key} {val}")
            any_agent = True
    if not any_agent:
        lines.append('supervisor_agent_chat_total{kind="general"} 0')

    # --- in-process: commands ---
    lines.append("# HELP supervisor_commands_total Comandos enviados por el operador")
    lines.append("# TYPE supervisor_commands_total counter")
    lines.append(f'supervisor_commands_total {inprocess.get("supervisor_commands_total", 0)}')

    lines.append("")  # línea final requerida por el formato
    return "\n".join(lines)


# ------------------------------------------------------------------ #
# Helpers de consulta directa a BD                                    #
# ------------------------------------------------------------------ #

def _count_proposals_by_status(store: "SupervisorStore") -> dict[str, int]:
    import sqlite3
    with store._connect() as conn:
        rows = conn.execute(
            "SELECT status, COUNT(*) as n FROM proposals GROUP BY status"
        ).fetchall()
    return {row["status"]: row["n"] for row in rows}


def _count_runs_by_status(store: "SupervisorStore") -> dict[str, int]:
    with store._connect() as conn:
        rows = conn.execute(
            "SELECT status, COUNT(*) as n FROM automation_runs GROUP BY status"
        ).fetchall()
    return {row["status"]: row["n"] for row in rows}


def _count_inbound(store: "SupervisorStore") -> int:
    with store._connect() as conn:
        row = conn.execute("SELECT COUNT(*) FROM inbound_events").fetchone()
    return int(row[0]) if row else 0
