# sm20_reduce.py - collapse a raw SM20 export into two LLM-ready files.
# Usage: python sm20_reduce.py sm20.txt
# Produces: sm20_summary.csv (volume picture) + sm20_signal.csv (security detail)
# Requires: pandas
import sys, pandas as pd

SIGNAL = {"AU2","AU4","AU5","AU6","AU7","AU8","AU9","AUA","AUB","AUD","AUE",
          "AUF","AUI","AUJ","AUL","AUM","AUN","AUY","BU1","BUJ","CUZ"}
PRIV   = {"SU01","SU10","PFCG","SE38","SE37","SE80","SE16","SE16N","SM30",
          "SM31","SM34","SM59","SM49","SM69","SCC4","SCC5","RZ10","RZ11",
          "STMS","SM01","SM12","SM19","SM20","SA38","SE93","SE11","ST05"}

raw  = open(sys.argv[1], encoding="utf-8", errors="replace").read().replace("\r\n", "\n").split("\n")
h    = next(i for i, l in enumerate(raw) if l.lstrip().startswith("SAP System"))
cols = [c.strip() or f"c{i}" for i, c in enumerate(raw[h].split("\t"))]
rows = [(l.split("\t") + [""] * len(cols))[:len(cols)]
        for l in raw[h+1:] if l.strip() and not l.lstrip().startswith("SAP System")]
df   = pd.DataFrame(rows, columns=cols).apply(lambda s: s.str.strip())

# File 1 - volume summary. Preserves the noise picture in a few hundred rows.
(df.groupby(["Event", "User", "Cl.", "Peer", "ABAP Source"])
   .size().reset_index(name="Count")
   .sort_values("Count", ascending=False)
   .to_csv("sm20_summary.csv", index=False))

# File 2 - signal only. The rows a human would actually read.
keep = (df["Event"].isin(SIGNAL)
        | (df["Event"].eq("DU9") & ~df["Audit Log Message"].str.contains("passed", na=False))
        | (df["Event"].eq("AU1") & df["Variable Data"].isin({"A", "H"}))
        | (df["Event"].eq("AU3") & df["Variable Data"].isin(PRIV)))
df[keep].to_csv("sm20_signal.csv", index=False)

print(f"{len(df):,} records -> summary "
      f"{df.groupby(['Event','User','Cl.','Peer','ABAP Source']).ngroups:,} rows | "
      f"signal {keep.sum():,} rows ({keep.mean():.2%})")
