"""Stathera integrity checks (gaps + overlaps) for heartbeat monitoring."""

from __future__ import annotations

import logging

from heartbeat.config import KNOWN_GAP_FINGERPRINTS
from heartbeat.state import load_stathera_counts, save_stathera_counts

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


def collect_stathera(
    client: object,  # clickhouse_connect Client
    results: dict[str, bool | str],
) -> None:
    """Run Stathera audit: detect BOTH gaps and overlaps in aggTrade IDs."""
    from opendeviationbar.clickhouse.stathera_query import (
        FULL_TABLE,
        cross_pair_filter,
        cross_pair_window,
    )

    # Check for pending async mutations
    pending_mutations_row = client.query(  # type: ignore[union-attr]
        "SELECT countIf(is_done = 0) FROM system.mutations "
        "WHERE database = 'opendeviationbar_cache'"
    )
    pending_mutations = (
        pending_mutations_row.result_rows[0][0]
        if pending_mutations_row.result_rows
        else 0
    )
    results["stathera_pending_mutations"] = str(pending_mutations)

    row = client.query(  # type: ignore[union-attr]
        f"""
        SELECT countIf(tid_delta < 0) AS overlap_count,
               countIf(tid_delta > 0) AS gap_count,
               countIf(tid_delta > 0 AND bar_date = prev_bar_date) AS intra_day_gaps,
               countIf(tid_delta > 0 AND bar_date - prev_bar_date = 1) AS adjacent_day_gaps
        FROM (
            SELECT first_agg_trade_id
                     - lagInFrame(last_agg_trade_id, 1) OVER w - 1
                     AS tid_delta,
                   row_number() OVER w AS rn,
                   toDate(close_time_ms / 1000) AS bar_date,
                   toDate(lagInFrame(close_time_ms, 1) OVER w / 1000) AS prev_bar_date
            FROM {FULL_TABLE} FINAL
            {cross_pair_filter()}
            {cross_pair_window()}
        )
        WHERE tid_delta != 0 AND rn > 1
        """
    )
    overlap_count = row.result_rows[0][0] if row.result_rows else 0
    gap_count = row.result_rows[0][1] if row.result_rows else 0
    intra_day_gaps = row.result_rows[0][2] if row.result_rows else 0
    adjacent_day_gaps = row.result_rows[0][3] if row.result_rows else 0

    # Delta tracking: compare with previous heartbeat to detect growth
    prev = load_stathera_counts()
    prev_overlaps = prev.get("overlap_count", overlap_count)
    prev_gaps = prev.get("gap_count", gap_count)
    overlap_delta = overlap_count - prev_overlaps
    gap_delta = gap_count - prev_gaps
    save_stathera_counts(overlap_count, gap_count)

    # Health logic
    kintsugi_healing = (
        results.get("kintsugi") is True
        and results.get("kintsugi_stuck") is not False
    )
    mutations_in_flight = pending_mutations > 0
    anomalies_growing = overlap_delta > 0 or gap_delta > 0
    has_anomalies = overlap_count > 0 or gap_count > 0
    # Real gaps = intra-day + adjacent-day (sidecar midnight drops).
    # Backfill gaps = everything else (multi-day spans with no data).
    real_gaps = intra_day_gaps + adjacent_day_gaps
    backfill_gaps_only = gap_count > 0 and real_gaps == 0

    if not has_anomalies or (mutations_in_flight and overlap_count > 0 and gap_count == 0) or (backfill_gaps_only and overlap_count == 0):
        results["stathera_overlaps"] = True
        results["stathera_gaps"] = True
    elif anomalies_growing and not kintsugi_healing:
        results["stathera_overlaps"] = overlap_count == 0
        results["stathera_gaps"] = real_gaps == 0
    else:
        results["stathera_overlaps"] = overlap_count == 0 or kintsugi_healing
        results["stathera_gaps"] = real_gaps == 0 or kintsugi_healing

    results["stathera_overlap_count"] = str(overlap_count)
    results["stathera_gap_count"] = str(gap_count)
    results["stathera_overlap_delta"] = str(overlap_delta)
    results["stathera_gap_delta"] = str(gap_delta)
    results["stathera_intra_day_gaps"] = str(intra_day_gaps)
    results["stathera_adjacent_day_gaps"] = str(adjacent_day_gaps)

    # Top 5 unique pairs per anomaly type
    if overlap_count > 0:
        top = client.query(  # type: ignore[union-attr]
            f"""
            SELECT concat(symbol, '@', toString(threshold_decimal_bps)) AS pair,
                   count() AS cnt
            FROM (
                SELECT symbol, threshold_decimal_bps,
                       first_agg_trade_id
                         - lagInFrame(last_agg_trade_id, 1) OVER w - 1
                         AS tid_delta,
                       row_number() OVER w AS rn
                FROM {FULL_TABLE} FINAL
                {cross_pair_filter()}
                {cross_pair_window()}
            )
            WHERE tid_delta < 0 AND rn > 1
            GROUP BY symbol, threshold_decimal_bps
            ORDER BY cnt DESC
            LIMIT 5
            """
        )
        pairs = [f"{r[0]}({r[1]})" for r in top.result_rows]
        if pairs:
            results["stathera_overlap_pairs"] = ", ".join(pairs)
    if gap_count > 0:
        top = client.query(  # type: ignore[union-attr]
            f"""
            SELECT concat(symbol, '@', toString(threshold_decimal_bps)) AS pair,
                   count() AS cnt
            FROM (
                SELECT symbol, threshold_decimal_bps,
                       first_agg_trade_id
                         - lagInFrame(last_agg_trade_id, 1) OVER w - 1
                         AS tid_delta,
                       row_number() OVER w AS rn
                FROM {FULL_TABLE} FINAL
                {cross_pair_filter()}
                {cross_pair_window()}
            )
            WHERE tid_delta > 0 AND rn > 1
            GROUP BY symbol, threshold_decimal_bps
            ORDER BY cnt DESC
            LIMIT 5
            """
        )
        pairs = [f"{r[0]}({r[1]})" for r in top.result_rows]
        if pairs:
            results["stathera_gap_pairs"] = ", ".join(pairs)

    # Fingerprint active gaps against known root causes
    if gap_count > 0:
        try:
            gap_detail = client.query(  # type: ignore[union-attr]
                f"""
                SELECT symbol, threshold_decimal_bps, prev_last_tid,
                       first_agg_trade_id, tid_delta
                FROM (
                    SELECT symbol, threshold_decimal_bps,
                           lagInFrame(last_agg_trade_id, 1) OVER w AS prev_last_tid,
                           first_agg_trade_id,
                           first_agg_trade_id
                             - lagInFrame(last_agg_trade_id, 1) OVER w - 1
                             AS tid_delta,
                           row_number() OVER w AS rn
                    FROM {FULL_TABLE} FINAL
                    {cross_pair_filter()}
                    {cross_pair_window()}
                )
                WHERE tid_delta > 0 AND rn > 1
                ORDER BY tid_delta DESC
                LIMIT 20
                """
            )
            fingerprints: list[dict] = []
            all_known = True
            for gap_row in gap_detail.result_rows:
                sym, thr, prev_tid, first_tid, tdelta = gap_row
                key = (sym, int(thr), int(first_tid), int(prev_tid))
                known = KNOWN_GAP_FINGERPRINTS.get(key)
                if known:
                    fingerprints.append(
                        {
                            "symbol": sym,
                            "threshold": int(thr),
                            "first_agg_trade_id": int(first_tid),
                            "prev_last_tid": int(prev_tid),
                            "tid_delta": int(tdelta),
                            **known,
                        }
                    )
                else:
                    all_known = False
            results["stathera_gap_fingerprints"] = fingerprints
            results["stathera_all_gaps_known"] = str(all_known and len(fingerprints) > 0)
        except Exception:
            logger.warning("Gap fingerprint query failed", exc_info=True)

    # (#264) Cross-midnight bar oracle
    cross_midnight = client.query(  # type: ignore[union-attr]
        f"""
        SELECT count() AS violations
        FROM {FULL_TABLE} FINAL
        WHERE toDate(open_time_ms / 1000, 'UTC')
           != toDate(close_time_ms / 1000, 'UTC')
        """
    )
    cross_midnight_count = (
        cross_midnight.result_rows[0][0] if cross_midnight.result_rows else 0
    )
    results["cross_midnight_bars"] = str(cross_midnight_count)
    if cross_midnight_count > 0:
        results["cross_midnight"] = cross_midnight_count == 0 or kintsugi_healing
        logger.warning(
            "Day-mode invariant violated: %d cross-midnight bars",
            cross_midnight_count,
        )
