#!/usr/bin/env python3
"""
FER Calibrate
Reads 7+ days of fer-log.jsonl, computes percentile-based thresholds,
and writes them to fer-state.json. Run manually or triggered by fer-monitor
after observe period ends.

Yellow = 5x the 75th percentile of 5-min output cost deltas
Red    = 10x the 95th percentile
"""

import json
import os
import sys
from datetime import datetime, timezone

LOG_FILE   = os.path.expanduser("~/.openclaw/workspace/memory/fer-log.jsonl")
STATE_FILE = os.path.expanduser("~/.openclaw/workspace/memory/fer-state.json")
MIN_SAMPLES = 50  # Refuse to calibrate on too little data


def percentile(data, p):
    if not data:
        return 0.0
    s = sorted(data)
    idx = int(len(s) * p / 100)
    return s[min(idx, len(s) - 1)]


def load_log():
    entries = []
    if not os.path.exists(LOG_FILE):
        return entries
    with open(LOG_FILE) as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                entries.append(json.loads(line))
            except json.JSONDecodeError:
                continue
    return entries


def load_state():
    if os.path.exists(STATE_FILE):
        with open(STATE_FILE) as f:
            return json.load(f)
    return {}


def save_state(state):
    with open(STATE_FILE, "w") as f:
        json.dump(state, f, indent=2)


def main():
    entries = load_log()
    # Use only non-zero output deltas from actual monitoring events (not gateway_unreachable)
    deltas = [
        e["delta_output_cost"]
        for e in entries
        if e.get("delta_output_cost", 0) > 0 and e.get("event") != "gateway_unreachable"
    ]

    print(f"Loaded {len(deltas)} valid delta samples from {len(entries)} log entries.")

    if len(deltas) < MIN_SAMPLES:
        print(f"Not enough data yet ({len(deltas)} < {MIN_SAMPLES} samples). "
              f"Keep observing — check back after more data accumulates.")
        sys.exit(0)

    p50 = percentile(deltas, 50)
    p75 = percentile(deltas, 75)
    p95 = percentile(deltas, 95)
    p99 = percentile(deltas, 99)

    yellow = p75 * 5.0
    red    = p95 * 10.0

    # Floor: never go below reasonable minimums regardless of how quiet usage has been
    yellow = max(yellow, 0.10)
    red    = max(red,    0.50)

    print(f"\nPercentiles (5-min output cost delta):")
    print(f"  p50: ${p50:.5f}")
    print(f"  p75: ${p75:.5f}")
    print(f"  p95: ${p95:.5f}")
    print(f"  p99: ${p99:.5f}")
    print(f"\nCalibrated thresholds:")
    print(f"  Yellow (alert):    ${yellow:.5f}/5min  (5x p75)")
    print(f"  Red    (critical): ${red:.5f}/5min  (10x p95)")

    state = load_state()
    state.setdefault("thresholds", {})
    state["thresholds"].update({
        "calibrated":    True,
        "calibrated_at": datetime.now(timezone.utc).isoformat(),
        "sample_size":   len(deltas),
        "p50_output":    round(p50, 6),
        "p75_output":    round(p75, 6),
        "p95_output":    round(p95, 6),
        "yellow_output": round(yellow, 6),
        "red_output":    round(red,    6),
    })

    save_state(state)
    print(f"\nState updated. Re-run fer-monitor.py to pick up new thresholds.")
    print(f"Mode will switch to 'enforce' automatically on next monitor run.")


if __name__ == "__main__":
    main()
