#!/usr/bin/env python3
"""Summarize stored OHLCV JSON files without sending raw bars to the LLM.

Usage:
  python3 hybrid/scripts/summarize_ohlcv.py hybrid/data/ohlcv_M15.json
  python3 hybrid/scripts/summarize_ohlcv.py hybrid/data/ohlcv_*.json --out hybrid/reports/ohlcv_summary.json

Input schema expected:
  {"metadata": {...}, "bars": [{"time", "open", "high", "low", "close", "volume"}, ...]}

Output is compact JSON/Markdown-sized statistics safe to paste into an LLM.
"""
from __future__ import annotations

import argparse
import json
import math
from datetime import datetime, timezone
from pathlib import Path
from statistics import mean


def ts_iso(ts):
    try:
        return datetime.fromtimestamp(int(ts), tz=timezone.utc).isoformat().replace("+00:00", "Z")
    except Exception:
        return None


def pct(a, b):
    if b in (0, None) or a is None:
        return None
    return (a - b) / b * 100.0


def summarize_file(path: Path) -> dict:
    data = json.loads(path.read_text())
    bars = data.get("bars") or []
    meta = data.get("metadata") or {}
    if not bars:
        return {"file": str(path), "metadata": meta, "bar_count": 0, "error": "no bars"}

    highs = [float(b["high"]) for b in bars if b.get("high") is not None]
    lows = [float(b["low"]) for b in bars if b.get("low") is not None]
    closes = [float(b["close"]) for b in bars if b.get("close") is not None]
    opens = [float(b["open"]) for b in bars if b.get("open") is not None]
    vols = [float(b.get("volume") or 0) for b in bars]
    ranges = [h - l for h, l in zip(highs, lows)]
    returns = [pct(closes[i], closes[i - 1]) for i in range(1, len(closes))]
    returns = [r for r in returns if r is not None and math.isfinite(r)]

    max_high = max(highs) if highs else None
    min_low = min(lows) if lows else None
    first = bars[0]
    last = bars[-1]
    last_close = closes[-1] if closes else None
    first_open = opens[0] if opens else None
    trend_pct = pct(last_close, first_open)

    # Compact swing/event hints: top/bottom 5 bars by high/low and largest ranges.
    top_highs = sorted(
        [{"time": b.get("time"), "iso": ts_iso(b.get("time")), "high": b.get("high")} for b in bars if b.get("high") is not None],
        key=lambda x: float(x["high"]), reverse=True,
    )[:5]
    bottom_lows = sorted(
        [{"time": b.get("time"), "iso": ts_iso(b.get("time")), "low": b.get("low")} for b in bars if b.get("low") is not None],
        key=lambda x: float(x["low"]),
    )[:5]
    largest_ranges = sorted(
        [
            {"time": b.get("time"), "iso": ts_iso(b.get("time")), "range": float(b.get("high", 0)) - float(b.get("low", 0)), "open": b.get("open"), "close": b.get("close")}
            for b in bars if b.get("high") is not None and b.get("low") is not None
        ],
        key=lambda x: x["range"], reverse=True,
    )[:10]

    return {
        "file": str(path),
        "metadata": meta,
        "bar_count": len(bars),
        "first_time": first.get("time"),
        "first_iso": ts_iso(first.get("time")),
        "last_time": last.get("time"),
        "last_iso": ts_iso(last.get("time")),
        "first_open": first_open,
        "last_close": last_close,
        "trend_pct": trend_pct,
        "high": max_high,
        "low": min_low,
        "avg_range": mean(ranges) if ranges else None,
        "avg_volume": mean(vols) if vols else None,
        "avg_return_pct": mean(returns) if returns else None,
        "top_highs": top_highs,
        "bottom_lows": bottom_lows,
        "largest_ranges": largest_ranges,
    }


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("files", nargs="+", help="OHLCV JSON files")
    ap.add_argument("--out", help="Write compact summary JSON to this path")
    args = ap.parse_args()
    result = {
        "generated_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
        "summaries": [summarize_file(Path(f)) for f in args.files],
    }
    text = json.dumps(result, indent=2, ensure_ascii=False)
    if args.out:
        out = Path(args.out)
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_text(text + "\n")
        print(f"WROTE {out} summaries={len(result['summaries'])}")
    else:
        print(text)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
