#!/usr/bin/env python3
"""Alert Triage with Elastic SIEM agent - queries Elasticsearch SIEM signals
index to retrieve, prioritize, and triage security alerts using
elasticsearch-py client."""

import argparse
import json
import sys
from collections import Counter
from datetime import datetime
from pathlib import Path

try:
    from elasticsearch import Elasticsearch
except ImportError:
    print("Install elasticsearch: pip install elasticsearch", file=sys.stderr)
    sys.exit(1)


SIEM_SIGNALS_INDEX = ".siem-signals-*"

SEVERITY_PRIORITY = {"critical": 1, "high": 2, "medium": 3, "low": 4}


def create_client(host: str, api_key: str = None, username: str = None,
                  password: str = None, verify_certs: bool = True) -> Elasticsearch:
    """Create Elasticsearch client."""
    kwargs = {"hosts": [host], "verify_certs": verify_certs}
    if api_key:
        kwargs["api_key"] = api_key
    elif username and password:
        kwargs["basic_auth"] = (username, password)
    return Elasticsearch(**kwargs)


def get_open_alerts(es: Elasticsearch, hours_back: int = 24,
                    severity: list[str] = None, size: int = 500) -> list[dict]:
    """Retrieve open SIEM alerts from the signals index."""
    must_clauses = [
        {"range": {"@timestamp": {"gte": f"now-{hours_back}h", "lte": "now"}}},
        {"term": {"signal.status": "open"}},
    ]
    if severity:
        must_clauses.append({"terms": {"signal.rule.severity": severity}})

    query = {"bool": {"must": must_clauses}}
    response = es.search(index=SIEM_SIGNALS_INDEX, query=query, size=size,
                         sort=[{"@timestamp": {"order": "desc"}}])
    alerts = []
    for hit in response["hits"]["hits"]:
        src = hit["_source"]
        signal = src.get("signal", {})
        rule = signal.get("rule", {})
        alerts.append({
            "alert_id": hit["_id"],
            "timestamp": src.get("@timestamp", ""),
            "rule_name": rule.get("name", ""),
            "rule_id": rule.get("id", ""),
            "severity": rule.get("severity", "unknown"),
            "risk_score": rule.get("risk_score", 0),
            "status": signal.get("status", ""),
            "source_ip": src.get("source", {}).get("ip", ""),
            "destination_ip": src.get("destination", {}).get("ip", ""),
            "user": src.get("user", {}).get("name", ""),
            "host": src.get("host", {}).get("name", ""),
            "process": src.get("process", {}).get("name", ""),
        })
    return alerts


def get_alert_aggregations(es: Elasticsearch, hours_back: int = 24) -> dict:
    """Aggregate alerts by rule, severity, and host."""
    query = {
        "bool": {
            "must": [
                {"range": {"@timestamp": {"gte": f"now-{hours_back}h"}}},
                {"term": {"signal.status": "open"}},
            ]
        }
    }
    aggs = {
        "by_severity": {"terms": {"field": "signal.rule.severity", "size": 10}},
        "by_rule": {"terms": {"field": "signal.rule.name.keyword", "size": 20}},
        "by_host": {"terms": {"field": "host.name.keyword", "size": 20}},
        "by_user": {"terms": {"field": "user.name.keyword", "size": 20}},
    }
    response = es.search(index=SIEM_SIGNALS_INDEX, query=query, aggs=aggs, size=0)
    result = {}
    for agg_name, agg_data in response.get("aggregations", {}).items():
        result[agg_name] = [
            {"key": bucket["key"], "count": bucket["doc_count"]}
            for bucket in agg_data.get("buckets", [])
        ]
    return result


def prioritize_alerts(alerts: list[dict]) -> list[dict]:
    """Sort and prioritize alerts by severity and risk score."""
    return sorted(alerts, key=lambda a: (
        SEVERITY_PRIORITY.get(a.get("severity", "low"), 5),
        -a.get("risk_score", 0)
    ))


def identify_alert_clusters(alerts: list[dict]) -> list[dict]:
    """Group related alerts that may represent a single incident."""
    clusters = []
    by_host = {}
    for alert in alerts:
        host = alert.get("host", "unknown")
        if host not in by_host:
            by_host[host] = []
        by_host[host].append(alert)

    for host, host_alerts in by_host.items():
        if len(host_alerts) >= 3:
            rules = list(set(a["rule_name"] for a in host_alerts))
            max_severity = min(host_alerts, key=lambda a: SEVERITY_PRIORITY.get(a.get("severity", "low"), 5))
            clusters.append({
                "host": host,
                "alert_count": len(host_alerts),
                "unique_rules": len(rules),
                "rules": rules[:10],
                "max_severity": max_severity["severity"],
                "recommendation": "Investigate as potential incident - multiple alerts on same host",
            })
    return clusters


def generate_report(host: str, api_key: str = None, username: str = None,
                    password: str = None, hours_back: int = 24,
                    severity: list[str] = None) -> dict:
    """Run alert triage and build consolidated report."""
    es = create_client(host, api_key, username, password)
    alerts = get_open_alerts(es, hours_back, severity)
    prioritized = prioritize_alerts(alerts)
    aggregations = get_alert_aggregations(es, hours_back)
    clusters = identify_alert_clusters(alerts)

    severity_counts = Counter(a["severity"] for a in alerts)
    return {
        "report": "elastic_siem_alert_triage",
        "generated_at": datetime.utcnow().isoformat() + "Z",
        "time_window_hours": hours_back,
        "total_open_alerts": len(alerts),
        "severity_summary": dict(severity_counts),
        "alert_clusters": clusters,
        "aggregations": aggregations,
        "prioritized_alerts": prioritized[:50],
    }


def main():
    parser = argparse.ArgumentParser(description="Elastic SIEM Alert Triage Agent")
    parser.add_argument("--host", required=True, help="Elasticsearch URL")
    parser.add_argument("--api-key", help="Elasticsearch API key")
    parser.add_argument("--username", help="Elasticsearch username")
    parser.add_argument("--password", help="Elasticsearch password")
    parser.add_argument("--hours", type=int, default=24, help="Hours to look back (default: 24)")
    parser.add_argument("--severity", nargs="+", help="Filter by severity levels")
    parser.add_argument("--output", help="Output JSON file path")
    args = parser.parse_args()

    report = generate_report(args.host, args.api_key, args.username,
                             args.password, args.hours, args.severity)
    output = json.dumps(report, indent=2)
    if args.output:
        Path(args.output).write_text(output, encoding="utf-8")
        print(f"Report written to {args.output}")
    else:
        print(output)


if __name__ == "__main__":
    main()
