"""Yesterday orphan completion check for heartbeat monitoring.

TELEM-02: Per-symbol validation that yesterday's orphan bar closes
at exactly crossover_tid - 1 (the last trade of the previous day).

MON-01: Aion symbols get a rolling-window freshness check instead
of the crossover_tid orphan check (Aion has no midnight reset).
"""

from __future__ import annotations

import logging
import time

from opendeviationbar.crossover import get_crossover_tid
from opendeviationbar.symbol_registry import get_symbol_entries

from heartbeat.config import AION_FRESHNESS_WINDOW_MIN, get_heartbeat_symbols

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


def collect_yesterday_completion(
    client: object,  # clickhouse_connect Client
    results: dict[str, bool | str],
) -> None:
    """Check yesterday's orphan bar completion per streaming symbol.

    For each enabled Binance spot symbol (via get_heartbeat_symbols()):
    - Day-mode symbols: crossover_tid orphan check (existing behavior)
    - Aion symbols: rolling-window freshness check (last bar within N min)
    """
    from opendeviationbar.clickhouse.stathera_query import FULL_TABLE

    # Load symbol entries for per-symbol mode lookup (safe fallback)
    try:
        entries = get_symbol_entries()
    except Exception:
        logger.warning(
            "Symbol registry load failed — all symbols treated as day-mode",
            exc_info=True,
        )
        entries = {}

    checked_symbols: list[str] = []

    for symbol in get_heartbeat_symbols():
        checked_symbols.append(symbol)

        # Determine ouroboros mode for this symbol
        entry = entries.get(symbol)
        mode = getattr(getattr(entry, "ouroboros_mode", None), "value", "day") if entry else "day"

        if mode == "aion":
            _check_aion_freshness(client, symbol, results, FULL_TABLE)
        else:
            _check_day_completion(client, symbol, results, FULL_TABLE)

    # Aggregate health gate: True unless a real mismatch (False) is detected
    results["yesterday_completion"] = all(
        results.get(f"yesterday_{s}") is not False for s in checked_symbols
    )


def _check_aion_freshness(
    client: object,
    symbol: str,
    results: dict[str, bool | str],
    full_table: str,
) -> None:
    """Rolling-window freshness check for Aion symbols.

    Queries the most recent close_time_us and checks if it's within
    the configurable AION_FRESHNESS_WINDOW_MIN window.
    """
    try:
        row = client.query(  # type: ignore[union-attr]
            f"""
            SELECT max(close_time_us)
            FROM {full_table} FINAL
            WHERE symbol = '{symbol}'
              AND close_time_us > 0
            """
        )
    except Exception:
        logger.warning(
            "Aion freshness query failed for %s", symbol, exc_info=True
        )
        results[f"yesterday_{symbol}"] = "query_failed"
        return

    max_close_us = row.result_rows[0][0] if row.result_rows else None
    if max_close_us is None:
        results[f"yesterday_{symbol}"] = "no_bars"
        return

    now_us = int(time.time() * 1_000_000)
    stale_min = (now_us - max_close_us) / 60_000_000

    if stale_min <= AION_FRESHNESS_WINDOW_MIN:
        results[f"yesterday_{symbol}"] = True
        results[f"yesterday_{symbol}_detail"] = (
            f"aion:fresh ({stale_min:.0f}m ago)"
        )
    else:
        results[f"yesterday_{symbol}"] = False
        results[f"yesterday_{symbol}_detail"] = (
            f"aion:stale ({stale_min:.0f}m, window={AION_FRESHNESS_WINDOW_MIN}m)"
        )


def _check_day_completion(
    client: object,
    symbol: str,
    results: dict[str, bool | str],
    full_table: str,
) -> None:
    """Day-mode orphan completion check (existing behavior).

    1. Get midnight crossover TID (first trade of today)
    2. Query yesterday's orphan bar from ClickHouse
    3. Compare: orphan.last_agg_trade_id should == crossover_tid - 1
    """
    # Step 1: Get crossover TID
    crossover_tid = get_crossover_tid(symbol)
    if crossover_tid is None:
        results[f"yesterday_{symbol}"] = "query_failed"
        return

    # Step 2: Query yesterday's LAST bar (the orphan — last bar before midnight)
    # No is_orphan column in CH; orphan = last bar of the day by trade ID
    try:
        row = client.query(  # type: ignore[union-attr]
            f"""
            SELECT max(last_agg_trade_id)
            FROM {full_table} FINAL
            WHERE symbol = '{symbol}'
              AND toDate(open_time_us / 1000000) = yesterday()
              AND last_agg_trade_id > 0
            """
        )
    except Exception:
        logger.warning(
            "yesterday orphan query failed for %s", symbol, exc_info=True
        )
        results[f"yesterday_{symbol}"] = "query_failed"
        return

    # Step 3: Compare
    orphan_tid = row.result_rows[0][0] if row.result_rows else None
    if orphan_tid is None:
        results[f"yesterday_{symbol}"] = "no_orphan"
    else:
        expected = crossover_tid - 1
        if orphan_tid == expected:
            results[f"yesterday_{symbol}"] = True
            results[f"yesterday_{symbol}_detail"] = "clean"
        else:
            results[f"yesterday_{symbol}"] = False
            results[f"yesterday_{symbol}_detail"] = (
                f"expected {expected}, got {orphan_tid}"
            )
