"""
Simplified shadow mode runner - sample & side-by-side, no data mutation
"""

from dataclasses import replace
import json
import logging
import random

logger = logging.getLogger(__name__)


def _get_match_count(result) -> int:
    """Safely extract match count from result"""
    if hasattr(result, "match_count"):
        return result.match_count
    if hasattr(result, "matches"):
        if hasattr(result.matches, "__len__"):
            return len(result.matches)
        elif hasattr(result.matches, "shape"):
            return result.matches.shape[0]
    return 0


def _get_max_score(result) -> float:
    """Safely extract max score from result"""
    if hasattr(result, "stats") and hasattr(result.stats, "max_score_observed"):
        return result.stats.max_score_observed
    if hasattr(result, "max_score"):
        return result.max_score
    if hasattr(result, "matches") and hasattr(result.matches, "score"):
        try:
            return float(result.matches["score"].max())
        except:
            pass
    return 0.0


def run_in_shadow(run_fn, cfg, src_df, ref_df, threshold: float):
    """
    Run legacy and new configurations side-by-side on sampled data.

    Args:
        run_fn: Pure function that runs matching (must not mutate inputs)
        cfg: Engine configuration
        src_df: Source DataFrame
        ref_df: Reference DataFrame
        threshold: Matching threshold
    """
    if not cfg.shadow_mode_enabled or cfg.shadow_mode_sample_pct <= 0:
        return

    try:
        # Deterministic sampling
        rng = random.Random(cfg.random_seed)
        sample_idx = (
            src_df.index.to_series()
            .sample(frac=cfg.shadow_mode_sample_pct, random_state=cfg.random_seed)
            .index
        )
        src_sample = src_df.loc[sample_idx].copy()

        # Clone configs for comparison
        legacy = replace(cfg, score_aggregator="weighted_mean")
        newcfg = replace(cfg, score_aggregator="weighted_present_mean")

        # Execute both paths (must be pure over inputs)
        legacy_res = run_fn(legacy, src_sample, ref_df)
        new_res = run_fn(newcfg, src_sample, ref_df)

        # Compare & log one JSONL line
        legacy_matches = _get_match_count(legacy_res)
        new_matches = _get_match_count(new_res)

        payload = {
            "shadow_sample": len(src_sample),
            "legacy": {"matches": legacy_matches, "max": _get_max_score(legacy_res)},
            "new": {"matches": new_matches, "max": _get_max_score(new_res)},
            "delta_matches": new_matches - legacy_matches,
        }
        logger.info("SHADOW %s", json.dumps(payload, ensure_ascii=True))

    except Exception as e:
        logger.warning(f"Shadow mode error: {e}")
        # Silent failure - don't break production
