#!/usr/bin/env python3
"""Agent for triaging security incidents using NIST SP 800-61 and SANS PICERL frameworks."""

import requests
import json
import argparse
from datetime import datetime, timezone


NIST_CATEGORIES = {
    "unauthorized_access": "Unauthorized Access",
    "dos": "Denial of Service",
    "malicious_code": "Malicious Code",
    "improper_usage": "Improper Usage",
    "reconnaissance": "Reconnaissance",
    "web_attack": "Web Application Attack",
}

SEVERITY_MATRIX = {
    "P1": {"label": "Critical", "ack_sla": "15 min", "contain_sla": "1 hour",
            "criteria": "Crown jewel compromise, active exfiltration, ransomware spreading"},
    "P2": {"label": "High", "ack_sla": "30 min", "contain_sla": "4 hours",
            "criteria": "Production compromise, confirmed malware, privileged account takeover"},
    "P3": {"label": "Medium", "ack_sla": "2 hours", "contain_sla": "24 hours",
            "criteria": "Non-production compromise, failed exploitation, single endpoint malware"},
    "P4": {"label": "Low", "ack_sla": "8 hours", "contain_sla": "72 hours",
            "criteria": "Reconnaissance, policy violation, benign true positive"},
}


def classify_incident(alert_data):
    """Classify incident type based on alert indicators."""
    print("[*] Classifying incident type...")
    alert_name = alert_data.get("alert_name", "").lower()
    process = alert_data.get("process", "").lower()
    event_code = alert_data.get("event_code", "")

    if any(kw in alert_name for kw in ["malware", "ransomware", "trojan", "cryptominer"]):
        category = "malicious_code"
    elif any(kw in alert_name for kw in ["brute force", "credential", "password spray"]):
        category = "unauthorized_access"
    elif any(kw in alert_name for kw in ["dos", "flood", "resource exhaustion"]):
        category = "dos"
    elif any(kw in alert_name for kw in ["sql injection", "xss", "ssrf", "xxe"]):
        category = "web_attack"
    elif any(kw in alert_name for kw in ["scan", "enum", "recon", "discovery"]):
        category = "reconnaissance"
    elif "powershell" in process and "encoded" in alert_name.lower():
        category = "malicious_code"
    else:
        category = "unauthorized_access"

    classification = NIST_CATEGORIES.get(category, "Unknown")
    print(f"  [+] Classification: {classification} ({category})")
    return category, classification


def assess_severity(alert_data, asset_criticality="medium"):
    """Calculate incident severity based on threat and asset context."""
    print("\n[*] Assessing severity...")
    threat_score = 0

    alert_severity = alert_data.get("severity", "").lower()
    if alert_severity in ("critical", "high"):
        threat_score += 3
    elif alert_severity == "medium":
        threat_score += 2
    else:
        threat_score += 1

    confidence = alert_data.get("confidence", 50)
    if confidence >= 80:
        threat_score += 2
    elif confidence >= 50:
        threat_score += 1

    asset_scores = {"critical": 3, "high": 2, "medium": 1, "low": 0}
    asset_score = asset_scores.get(asset_criticality, 1)

    total = threat_score + asset_score
    if total >= 7:
        priority = "P1"
    elif total >= 5:
        priority = "P2"
    elif total >= 3:
        priority = "P3"
    else:
        priority = "P4"

    sev_info = SEVERITY_MATRIX[priority]
    print(f"  [+] Priority: {priority} - {sev_info['label']}")
    print(f"  [+] ACK SLA: {sev_info['ack_sla']} | Containment SLA: {sev_info['contain_sla']}")
    return priority, sev_info


def check_virustotal(api_key, indicator, indicator_type="ip"):
    """Check an indicator against VirusTotal."""
    print(f"\n[*] Checking VirusTotal for {indicator_type}: {indicator}...")
    base_urls = {
        "ip": f"https://www.virustotal.com/api/v3/ip_addresses/{indicator}",
        "hash": f"https://www.virustotal.com/api/v3/files/{indicator}",
        "domain": f"https://www.virustotal.com/api/v3/domains/{indicator}",
    }
    url = base_urls.get(indicator_type)
    if not url:
        return {}
    try:
        headers = {"x-apikey": api_key}
        resp = requests.get(url, headers=headers, timeout=15)
        if resp.status_code == 200:
            data = resp.json().get("data", {}).get("attributes", {})
            if indicator_type in ("ip", "domain"):
                malicious = data.get("last_analysis_stats", {}).get("malicious", 0)
                total = sum(data.get("last_analysis_stats", {}).values())
                print(f"  [+] VT Result: {malicious}/{total} vendors flagged as malicious")
                return {"malicious": malicious, "total": total}
            elif indicator_type == "hash":
                malicious = data.get("last_analysis_stats", {}).get("malicious", 0)
                name = data.get("meaningful_name", "Unknown")
                print(f"  [+] VT Result: {name} - {malicious} detections")
                return {"name": name, "malicious": malicious}
        elif resp.status_code == 404:
            print(f"  [-] Not found in VirusTotal")
        else:
            print(f"  [-] VT API error: {resp.status_code}")
    except requests.RequestException as e:
        print(f"  [-] VT request failed: {e}")
    return {}


def build_mitre_mapping(category, process_info=""):
    """Map incident to MITRE ATT&CK techniques."""
    mappings = {
        "malicious_code": [
            {"technique": "T1059.001", "name": "PowerShell"},
            {"technique": "T1204.002", "name": "User Execution: Malicious File"},
        ],
        "unauthorized_access": [
            {"technique": "T1110", "name": "Brute Force"},
            {"technique": "T1078", "name": "Valid Accounts"},
        ],
        "reconnaissance": [
            {"technique": "T1046", "name": "Network Service Discovery"},
            {"technique": "T1595", "name": "Active Scanning"},
        ],
        "web_attack": [
            {"technique": "T1190", "name": "Exploit Public-Facing Application"},
        ],
    }
    techniques = mappings.get(category, [])
    if techniques:
        print(f"\n[*] MITRE ATT&CK mapping:")
        for t in techniques:
            print(f"  - {t['technique']}: {t['name']}")
    return techniques


def generate_triage_record(alert_data, classification, priority, sev_info,
                            ti_results, mitre, output_path):
    """Generate a structured incident triage report."""
    triage_time = datetime.now(timezone.utc)
    alert_time = alert_data.get("timestamp", triage_time.isoformat())

    record = {
        "ticket_id": f"INC-{triage_time.strftime('%Y')}-{hash(str(alert_data)) % 10000:04d}",
        "triage_analyst": alert_data.get("analyst", "automated"),
        "triage_time": triage_time.isoformat(),
        "alert_time": alert_time,
        "classification": {
            "type": classification[1],
            "category": classification[0],
            "priority": priority,
            "severity_label": sev_info["label"],
            "confidence": alert_data.get("confidence", "Unknown"),
        },
        "affected_scope": {
            "assets": alert_data.get("affected_hosts", []),
            "users": alert_data.get("affected_users", []),
            "business_unit": alert_data.get("business_unit", "Unknown"),
        },
        "evidence": {
            "alert_source": alert_data.get("source", "Unknown"),
            "alert_name": alert_data.get("alert_name", "Unknown"),
            "raw_indicators": alert_data.get("indicators", {}),
        },
        "enrichment": {
            "threat_intel": ti_results,
            "mitre_attack": mitre,
        },
        "recommended_actions": [],
        "sla": {
            "acknowledge_by": sev_info["ack_sla"],
            "contain_by": sev_info["contain_sla"],
        },
    }

    if priority in ("P1", "P2"):
        record["recommended_actions"] = [
            "Isolate affected endpoint via EDR",
            "Disable compromised user account",
            "Preserve volatile evidence (memory dump)",
            "Escalate to Tier 2 IR team",
        ]
    else:
        record["recommended_actions"] = [
            "Monitor for additional indicators",
            "Review related alerts for the past 7 days",
            "Document findings and close if benign",
        ]

    with open(output_path, "w") as f:
        json.dump(record, f, indent=2)
    print(f"\n[*] Triage record saved to {output_path}")
    print(f"[*] Ticket: {record['ticket_id']} | Priority: {priority} ({sev_info['label']})")
    return record


def main():
    parser = argparse.ArgumentParser(description="Security Incident Triage Agent")
    parser.add_argument("--alert-name", required=True, help="Name of the triggering alert")
    parser.add_argument("--source", default="SIEM", help="Alert source system")
    parser.add_argument("--severity", default="high", help="Alert severity")
    parser.add_argument("--confidence", type=int, default=75, help="Alert confidence (0-100)")
    parser.add_argument("--host", help="Affected hostname")
    parser.add_argument("--src-ip", help="Source IP address")
    parser.add_argument("--user", help="Affected username")
    parser.add_argument("--process", default="", help="Suspicious process name")
    parser.add_argument("--asset-criticality", default="medium",
                        choices=["critical", "high", "medium", "low"])
    parser.add_argument("--vt-key", help="VirusTotal API key for threat intel")
    parser.add_argument("--indicator", help="IOC to check (IP, hash, or domain)")
    parser.add_argument("--indicator-type", default="ip", choices=["ip", "hash", "domain"])
    parser.add_argument("-o", "--output", default="triage_record.json")
    args = parser.parse_args()

    alert_data = {
        "alert_name": args.alert_name,
        "source": args.source,
        "severity": args.severity,
        "confidence": args.confidence,
        "affected_hosts": [args.host] if args.host else [],
        "affected_users": [args.user] if args.user else [],
        "process": args.process,
        "indicators": {"src_ip": args.src_ip},
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }

    print("[*] Security Incident Triage\n")
    classification = classify_incident(alert_data)
    priority, sev_info = assess_severity(alert_data, args.asset_criticality)
    mitre = build_mitre_mapping(classification[0], args.process)

    ti_results = {}
    if args.vt_key and args.indicator:
        ti_results = check_virustotal(args.vt_key, args.indicator, args.indicator_type)

    generate_triage_record(alert_data, classification, priority, sev_info,
                           ti_results, mitre, args.output)


if __name__ == "__main__":
    main()
