#!/usr/bin/env python3
"""Event alerter detector.

Finds events that ended in the last 30-90 minutes (configurable), filters
out ones we've already alerted on (per 07-calendar/alerted.log), and prints
the remaining events as JSON for the LLM cron job to consume.

Output format: JSON array of events, each with:
  {id, summary, dtstart_utc, dtend_utc, tier, source_url, source_note}

Usage:
  python3 scripts/event_alerter.py            # default 30-90 min window
  python3 scripts/event_alerter.py --since 30 --lookback 90
  python3 scripts/event_alerter.py --mark    # also write IDs to alerted.log
"""
from __future__ import annotations

import argparse
import json
import re
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path

VAULT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(VAULT / "scripts"))

from data.calendar_parse import events_recently_ended  # noqa: E402
from data.prices import fetch_event_reaction  # noqa: E402

ALERTED_LOG = VAULT / "07-calendar" / "alerted.log"
EVENTS_YAML = VAULT / "07-calendar" / "events.yaml"


def load_alerted_log() -> set[str]:
    if not ALERTED_LOG.exists():
        return set()
    out: set[str] = set()
    for line in ALERTED_LOG.read_text().splitlines():
        line = line.strip()
        if not line or line.startswith("#"):
            continue
        # Format: ISO-timestamp  EVENT-ID
        parts = line.split(None, 1)
        if len(parts) == 2:
            out.add(parts[1])
    return out


def append_alerted_log(event_id: str) -> None:
    ALERTED_LOG.parent.mkdir(parents=True, exist_ok=True)
    with ALERTED_LOG.open("a") as f:
        f.write(f"{datetime.now(timezone.utc).isoformat(timespec='seconds')}  {event_id}\n")


def load_event_source(event_id: str) -> tuple[str, str]:
    """Return (source_url, source_note) for an event_id by parsing events.yaml.
    Returns ("", "") if not found."""
    if not EVENTS_YAML.exists():
        return ("", "")
    try:
        import yaml
    except ImportError:
        return ("", "")
    try:
        with EVENTS_YAML.open() as f:
            data = yaml.safe_load(f)
    except Exception:
        return ("", "")
    for ev in data.get("events", []):
        if ev.get("id") == event_id:
            return (ev.get("source", "") or "", ev.get("notes", "") or "")
    return ("", "")


def reaction_block(event: dict) -> dict:
    """Compute market reaction for the event window. dtstart_utc is the
    actual event time; we look at +1h after that for the reaction."""
    dtstart = event["dtstart_utc"]
    return {
        "event_time_utc": dtstart.isoformat(),
        "reaction": fetch_event_reaction(dtstart, window_hours=1),
    }


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--since", type=int, default=30,
                        help="Events that ended at least N min ago (default 30)")
    parser.add_argument("--lookback", type=int, default=90,
                        help="Events that ended at most N min ago (default 90)")
    parser.add_argument("--mark", action="store_true",
                        help="Append event IDs to alerted.log (only do this after analysis succeeds)")
    parser.add_argument("--tier-min", type=int, default=1,
                        help="Minimum tier to include (1, 2, or 0 for all)")
    parser.add_argument("--include-structural", action="store_true",
                        help="Also include structural events (default: only Tier 1/2)")
    parser.add_argument("--structural-ids", default="cot-friday",
                        help="Comma-separated structural event ID prefixes to alert on "
                             "(default: cot-friday). Use empty string to disable.")
    parser.add_argument("--include-reaction", action="store_true",
                        help="Pre-fetch market reaction for each event (slower)")
    parser.add_argument("--dry-run", action="store_true",
                        help="Don't mark or fetch; just list what would be alerted")
    args = parser.parse_args()

    now = datetime.now(timezone.utc)
    raw_events = events_recently_ended(lookback_minutes=args.lookback,
                                       since_minutes=args.since, now=now)
    # Filter by tier and structural allowlist
    structural_prefixes = [s.strip() for s in args.structural_ids.split(",") if s.strip()]
    filtered = []
    for e in raw_events:
        if isinstance(e["tier"], int):
            if e["tier"] >= args.tier_min:
                filtered.append(e)
        elif e["tier"] == "structural":
            if args.include_structural and any(e["id"].startswith(p) for p in structural_prefixes):
                filtered.append(e)
    raw_events = filtered
    # Filter by alerted state
    already = load_alerted_log()
    new_events = [e for e in raw_events if e["id"] not in already]
    if not new_events:
        print(json.dumps({"alerts": [], "now": now.isoformat(),
                          "skipped_already_alerted": [e["id"] for e in raw_events]}))
        return 0
    out_events = []
    for e in new_events:
        source_url, source_note = load_event_source(e["id"])
        item = {
            "id": e["id"],
            "summary": e["summary"],
            "dtstart_utc": e["dtstart_utc"].isoformat(),
            "dtend_utc": e["dtend_utc"].isoformat(),
            "tier": e["tier"],
            "source_url": source_url,
            "source_note": source_note,
        }
        if args.include_reaction:
            item["reaction"] = reaction_block(e)
        out_events.append(item)
    if args.dry_run:
        print(json.dumps({"alerts": out_events, "dry_run": True,
                          "now": now.isoformat()}, indent=2))
        return 0
    if args.mark:
        for e in new_events:
            append_alerted_log(e["id"])
    print(json.dumps({"alerts": out_events, "marked": args.mark,
                      "now": now.isoformat()}, indent=2))
    return 0


if __name__ == "__main__":
    sys.exit(main())
