"""Weekly Vision vs REST trade count oracle check (D-10).

Compares trade counts from local Parquet (Vision-sourced) against REST API
for a sample of days/symbols. Alerts if mismatch exceeds 0.1%.
"""

from __future__ import annotations

import contextlib
import json
import logging
import random
import time
from datetime import UTC, datetime, timedelta
from pathlib import Path

import polars as pl

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

_LAST_CHECK_FILE = Path("/var/tmp/opendeviationbar-vision-oracle-last.json")
_CHECK_INTERVAL_DAYS = 7
_MISMATCH_THRESHOLD = 0.001  # 0.1%
_SAMPLE_DAYS = 5
_SAMPLE_SYMBOLS = ["BTCUSDT", "ETHUSDT", "SOLUSDT"]


def check_vision_oracle(results: dict[str, bool | str]) -> None:
    """Compare Vision (Parquet) vs REST trade counts weekly (D-10).

    ``vision_oracle_ok`` participates in the healthy gate only when a check runs.
    On non-check weeks, set True (no-op).
    """
    # Check if we've run recently
    try:
        last = json.loads(_LAST_CHECK_FILE.read_text())
        last_ts = last.get("checked_at", 0)
        if time.time() - last_ts < _CHECK_INTERVAL_DAYS * 86400:
            results["vision_oracle_ok"] = True
            results["vision_oracle_status"] = "skipped (checked recently)"
            return
    except (FileNotFoundError, json.JSONDecodeError, OSError):
        pass  # First run or corrupt state

    try:
        from opendeviationbar._core import fetch_aggtrades_rest
        from opendeviationbar.storage.parquet import TickStorage

        storage = TickStorage()
        today = datetime.now(UTC).date()

        # Sample random completed days (8-30 days ago, Vision-confirmed range)
        candidate_days = [today - timedelta(days=d) for d in range(8, 31)]
        sample_days = random.sample(candidate_days, min(_SAMPLE_DAYS, len(candidate_days)))

        mismatches = []
        checked = 0

        for sym in _SAMPLE_SYMBOLS:
            for check_date in sample_days:
                date_str = check_date.isoformat()
                parquet_path = storage._get_parquet_path(sym, date_str)
                if not parquet_path.exists():
                    continue

                # Get Vision (Parquet) trade count
                vision_count = pl.scan_parquet(parquet_path).select(pl.len()).collect().item()

                # Get REST trade count (first+last ID, compute count from range)
                day_start_ms = int(
                    datetime.combine(check_date, datetime.min.time(), tzinfo=UTC).timestamp()
                    * 1000
                )
                day_end_ms = day_start_ms + 86_400_000 - 1

                first_trades = fetch_aggtrades_rest(sym, day_start_ms, day_start_ms + 60_000)
                last_trades = fetch_aggtrades_rest(sym, day_end_ms - 60_000, day_end_ms)
                if not first_trades or not last_trades:
                    continue

                rest_count = last_trades[-1]["a"] - first_trades[0]["a"] + 1

                # Compare
                if rest_count > 0:
                    mismatch = abs(vision_count - rest_count) / rest_count
                    if mismatch > _MISMATCH_THRESHOLD:
                        mismatches.append(
                            f"{sym}/{date_str}: vision={vision_count} rest={rest_count} "
                            f"({mismatch:.2%})"
                        )
                checked += 1

        # Save check timestamp
        with contextlib.suppress(OSError):
            _LAST_CHECK_FILE.write_text(
                json.dumps({
                    "checked_at": time.time(),
                    "checked_count": checked,
                    "mismatches": len(mismatches),
                })
            )

        if mismatches:
            results["vision_oracle_ok"] = False
            results["vision_oracle_alert"] = (
                f"{len(mismatches)} mismatches: " + "; ".join(mismatches[:3])
            )
        else:
            results["vision_oracle_ok"] = True
            results["vision_oracle_status"] = f"checked {checked} pairs, all within 0.1%"

    except Exception as exc:
        results["vision_oracle_ok"] = True  # Don't block on oracle failure
        results["vision_oracle_status"] = f"error: {exc}"
