"""Kintsugi NDJSON telemetry parsing for heartbeat monitoring."""

from __future__ import annotations

import json
import logging
from datetime import datetime, timezone
from pathlib import Path
from typing import TypedDict

from heartbeat.checks.services import get_kintsugi_daemon_pid

logger = logging.getLogger("health-heartbeat")


class KintsugiNdjsonResult(TypedDict):
    daemon_age_h: float | None
    stuck_repair: str | None
    total_shards: int | None
    active_repair: str | None  # in-flight repair (< 1h, not yet stuck)
    recent_healed: list[str]   # last 3 healed repairs: "BTCUSDT@250 91 bars"


def parse_kintsugi_ndjson(
    ndjson: Path,
) -> KintsugiNdjsonResult:
    """Parse kintsugi NDJSON for daemon freshness, stuck detection, and shard stats.

    Returns dict with:
      daemon_age_h: hours since last daemon event (None if no PID-tagged events)
      stuck_repair: description of stuck repair, or None if healthy
      total_shards: total shard count from latest discovery (None if unavailable)
    """
    result = KintsugiNdjsonResult(
        daemon_age_h=None,
        stuck_repair=None,
        total_shards=None,
        active_repair=None,
        recent_healed=[],
    )
    if not ndjson.exists():
        return result

    # Read tail of file (last 100 lines — cheap even on large files)
    try:
        raw = ndjson.read_text()
    except OSError:
        return result
    lines = raw.strip().split("\n")
    tail = lines[-100:]

    events = []
    for line in tail:
        try:
            events.append(json.loads(line))
        except (json.JSONDecodeError, ValueError):
            continue

    if not events:
        return result

    daemon_pid = get_kintsugi_daemon_pid()
    now = datetime.now(timezone.utc)

    # Filter to daemon PID if available, otherwise use all events
    if daemon_pid is not None:
        pid_events = [e for e in events if e.get("pid") == daemon_pid]
        if pid_events:
            events = pid_events
        # else: pre-upgrade events without pid — use all

    # Daemon freshness: age of latest event
    try:
        last_ts = datetime.fromisoformat(events[-1]["ts"])
        result["daemon_age_h"] = (now - last_ts).total_seconds() / 3600
    except (KeyError, ValueError):
        pass

    # Stuck/active repair: unmatched repair_start (current daemon only)
    starts = [e for e in events if e.get("event") == "repair_start"]
    completes = [e for e in events if e.get("event") == "repair_complete"]
    if len(starts) > len(completes):
        last_start = starts[-1]
        try:
            start_ts = datetime.fromisoformat(last_start["ts"])
            elapsed_m = (now - start_ts).total_seconds() / 60
            sym = last_start.get("symbol", "?")
            thr = last_start.get("threshold", "?")
            gap_h = last_start.get("gap_hours")
            gap_suffix = f" {gap_h:.0f}h gap" if gap_h is not None else ""
            if elapsed_m > 120:
                result["stuck_repair"] = f"{sym}@{thr} {elapsed_m / 60:.1f}h"
            else:
                result["active_repair"] = (
                    f"{sym}@{thr}{gap_suffix}, {elapsed_m:.0f}m"
                )
        except (KeyError, ValueError):
            pass

    # Recent healed repairs (last 3, for peace-of-mind progress display)
    healed = [
        e for e in events
        if e.get("event") == "repair_complete" and e.get("result") == "healed"
    ]
    for e in healed[-3:]:
        sym = e.get("symbol", "?")
        thr = e.get("threshold", "?")
        bars = e.get("bars_written", "?")
        result["recent_healed"].append(f"{sym}@{thr} {bars} bars")

    # Latest discovery stats (shard queue depth)
    for e in reversed(events):
        if e.get("event") == "discovery_complete":
            # Backward-compat: old NDJSON had p1+p2, new has total_shards
            result["total_shards"] = e.get(
                "total_shards", e.get("p1", 0) + e.get("p2", 0)
            )
            break

    return result
