# Issue #318: Rust CH writer feature flag helpers (Phase 37, INTEG-03)
"""Feature flag routing for Rust ClickHouse writer integration.

The OPENDEVIATIONBAR_USE_RUST_CH_WRITER env var controls which write path
the sidecar uses:
  - "off"    : Python-only CH writes (current behavior, default)
  - "shadow" : Dual-write with shadow comparison
  - "live"   : Rust-only CH writes (Python skips _write_batch_with_retry)

The flag is evaluated exactly once at _create_engine() startup -- no
mid-session env var re-reads. The value flows through the return tuple
to all consumers.
"""
from __future__ import annotations

import logging
from typing import Any

logger = logging.getLogger(__name__)

_VALID_RUST_CH_WRITER_MODES = frozenset({"off", "shadow", "live"})


def _validate_rust_ch_writer_mode(mode: str) -> str:
    """Validate and normalize the Rust CH writer mode string.

    Returns a valid mode ("off", "shadow", "live"). Invalid values
    default to "off" with a warning.
    """
    normalized = mode.lower()
    if normalized not in _VALID_RUST_CH_WRITER_MODES:
        logger.warning(
            "Invalid OPENDEVIATIONBAR_USE_RUST_CH_WRITER=%r, defaulting to 'off'",
            mode,
        )
        return "off"
    return normalized


def _should_python_write(rust_ch_writer_mode: str) -> bool:
    """Return True if Python should write bars to ClickHouse.

    In "live" mode, Rust handles all CH writes via fan-out -- Python skips
    _write_batch_with_retry() entirely. In "off" and "shadow" modes,
    Python writes normally.
    """
    return rust_ch_writer_mode != "live"


def _should_shadow_compare(rust_ch_writer_mode: str) -> bool:
    """Return True if shadow comparison should run after Python writes.

    Only active in "shadow" mode -- dual-write validation where both
    Rust and Python write, and we compare results.
    """
    return rust_ch_writer_mode == "shadow"


def _shadow_compare(
    symbol: str,
    threshold: int,
    bars: list[dict[str, Any]],
    cache: object | None,
) -> None:
    """Shadow comparison: query CH for bar/dedup_token counts in TID range.

    In shadow mode, both Rust and Python write bars. This function
    verifies consistency by checking count() and countDistinct(dedup_token) in
    the TID range of the batch just written. Does NOT use FINAL (expensive).
    """
    if cache is None or not bars:
        return
    first_tid = min(b.get("first_agg_trade_id", 0) for b in bars)
    last_tid = max(b.get("last_agg_trade_id", 0) for b in bars)
    if first_tid <= 0 or last_tid <= 0:
        return
    try:
        result = cache.client.query(
            "SELECT count() AS cnt, countDistinct(dedup_token) AS uniq "
            "FROM opendeviationbar_cache.open_deviation_bars "
            f"WHERE symbol = '{symbol}' AND threshold_decimal_bps = {threshold} "
            f"AND first_agg_trade_id >= {first_tid} "
            f"AND last_agg_trade_id <= {last_tid}"
        )
        if result.result_rows:
            cnt, uniq = result.result_rows[0]
            expected = len(bars)
            if cnt != expected or uniq != expected:
                logger.warning(
                    "shadow-compare MISMATCH %s@%d: expected=%d, ch_count=%d, "
                    "ch_unique_dedup=%d (tid_range=%d..%d)",
                    symbol, threshold, expected, cnt, uniq, first_tid, last_tid,
                )
            else:
                logger.debug(
                    "shadow-compare OK %s@%d: %d bars, %d unique dedup_tokens",
                    symbol, threshold, cnt, uniq,
                )
    except (OSError, RuntimeError, ConnectionError):
        logger.warning(
            "shadow-compare: CH query failed for %s@%d (non-fatal)",
            symbol, threshold, exc_info=True,
        )
