#!/usr/bin/env python3
"""Summarize causal replacement-shadow decisions from enriched block logs."""

from __future__ import annotations

import argparse
import json
import re
from collections import Counter
from datetime import datetime, timezone
from pathlib import Path

LINE_RE = re.compile(
    r"^(?P<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*"
    r"entry_blocked reason=(?P<reason>\S+) pair=(?P<pair>\S+) side=(?P<side>\S+) "
    r"occupied=(?P<occupied>\S+) candidate=(?P<candidate>\S+) "
    r"occupied_state=(?P<occupied_state>\S+) replacement_shadow=(?P<shadow>\S+)"
)


def parse_line(line: str) -> dict | None:
    match = LINE_RE.search(line)
    if not match:
        return None
    return {
        "ts": datetime.strptime(match.group("ts"), "%Y-%m-%d %H:%M:%S").replace(
            tzinfo=timezone.utc
        ),
        "reason": match.group("reason"),
        "pair": match.group("pair"),
        "side": match.group("side"),
        "occupied": match.group("occupied"),
        "candidate": json.loads(match.group("candidate")),
        "occupied_state": json.loads(match.group("occupied_state")),
        "shadow": json.loads(match.group("shadow")),
    }


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("logs", nargs="+", type=Path)
    parser.add_argument("--since", help="ISO-8601 lower bound")
    args = parser.parse_args()
    since = datetime.fromisoformat(args.since.replace("Z", "+00:00")) if args.since else None

    rows = []
    for path in args.logs:
        for line in path.read_text(errors="replace").splitlines():
            row = parse_line(line)
            if row and (since is None or row["ts"] >= since):
                rows.append(row)
    rows.sort(key=lambda row: row["ts"])
    eligible = [row for row in rows if row["shadow"].get("eligible") is True]
    report = {
        "events": len(rows),
        "eligible": len(eligible),
        "eligible_rate": len(eligible) / len(rows) if rows else None,
        "by_reason": Counter(row["shadow"].get("reason") for row in rows),
        "eligible_by_pair": Counter(row["pair"] for row in eligible),
        "first": rows[0]["ts"].isoformat() if rows else None,
        "last": rows[-1]["ts"].isoformat() if rows else None,
    }
    print(json.dumps(report, indent=2, default=dict))


if __name__ == "__main__":
    main()
