#!/usr/bin/env python3
"""Agent for implementing API abuse detection with rate limiting analysis."""

import json
import argparse
from datetime import datetime
from collections import defaultdict, Counter


def load_access_logs(log_path):
    """Load API access logs from JSON lines."""
    entries = []
    with open(log_path) as f:
        for line in f:
            try:
                entries.append(json.loads(line))
            except json.JSONDecodeError:
                continue
    return entries


def detect_brute_force(logs, failure_threshold=10, window_minutes=5):
    """Detect brute force attacks by counting auth failures per IP."""
    ip_failures = defaultdict(list)
    for entry in logs:
        status = entry.get("status_code", entry.get("status", 0))
        if int(status) in (401, 403):
            ip = entry.get("client_ip", entry.get("ip", ""))
            ts = entry.get("timestamp", "")
            ip_failures[ip].append(ts)
    findings = []
    for ip, timestamps in ip_failures.items():
        if len(timestamps) >= failure_threshold:
            findings.append({
                "client_ip": ip,
                "auth_failures": len(timestamps),
                "severity": "CRITICAL" if len(timestamps) > 50 else "HIGH",
                "category": "brute_force",
                "first_seen": timestamps[0],
                "last_seen": timestamps[-1],
            })
    return sorted(findings, key=lambda x: x["auth_failures"], reverse=True)


def detect_api_scraping(logs, threshold=500):
    """Detect API scraping by high request volume per IP."""
    ip_counts = Counter()
    ip_endpoints = defaultdict(set)
    for entry in logs:
        ip = entry.get("client_ip", entry.get("ip", ""))
        endpoint = entry.get("path", entry.get("endpoint", ""))
        ip_counts[ip] += 1
        ip_endpoints[ip].add(endpoint)
    findings = []
    for ip, count in ip_counts.items():
        if count >= threshold:
            findings.append({
                "client_ip": ip,
                "total_requests": count,
                "unique_endpoints": len(ip_endpoints[ip]),
                "severity": "HIGH",
                "category": "api_scraping",
            })
    return sorted(findings, key=lambda x: x["total_requests"], reverse=True)


def detect_credential_stuffing(logs, threshold=20):
    """Detect credential stuffing: many unique usernames from single IP."""
    ip_users = defaultdict(set)
    for entry in logs:
        if entry.get("path", "").endswith(("/login", "/auth", "/signin")):
            ip = entry.get("client_ip", entry.get("ip", ""))
            user = entry.get("username", entry.get("user", ""))
            if user:
                ip_users[ip].add(user)
    findings = []
    for ip, users in ip_users.items():
        if len(users) >= threshold:
            findings.append({
                "client_ip": ip,
                "unique_usernames": len(users),
                "severity": "CRITICAL",
                "category": "credential_stuffing",
            })
    return sorted(findings, key=lambda x: x["unique_usernames"], reverse=True)


def detect_rate_limit_bypass(logs):
    """Detect attempts to bypass rate limiting."""
    findings = []
    ip_ua_combos = defaultdict(set)
    for entry in logs:
        ip = entry.get("client_ip", entry.get("ip", ""))
        ua = entry.get("user_agent", "")
        ip_ua_combos[ip].add(ua)
    for ip, agents in ip_ua_combos.items():
        if len(agents) >= 10:
            findings.append({
                "client_ip": ip,
                "unique_user_agents": len(agents),
                "severity": "HIGH",
                "category": "ua_rotation",
                "reason": "Rotating User-Agent to bypass rate limits",
            })
    ip_429_count = Counter()
    for entry in logs:
        if int(entry.get("status_code", entry.get("status", 0))) == 429:
            ip = entry.get("client_ip", entry.get("ip", ""))
            ip_429_count[ip] += 1
    for ip, count in ip_429_count.items():
        if count >= 50:
            findings.append({
                "client_ip": ip,
                "rate_limit_hits": count,
                "severity": "MEDIUM",
                "category": "rate_limit_persistence",
                "reason": "Continuing requests after rate limiting",
            })
    return findings


def generate_rate_limit_config(logs):
    """Generate recommended rate limit configuration based on traffic patterns."""
    endpoint_counts = Counter()
    for entry in logs:
        path = entry.get("path", entry.get("endpoint", ""))
        endpoint_counts[path] += 1
    auth_endpoints = [p for p in endpoint_counts if any(
        k in p for k in ["login", "auth", "signin", "register", "password"])]
    config = {
        "global": {"requests_per_minute": 100, "burst": 20},
        "auth_endpoints": {
            "endpoints": auth_endpoints,
            "requests_per_minute": 10,
            "burst": 3,
            "block_duration_seconds": 300,
        },
        "sensitive_endpoints": {
            "endpoints": ["/api/admin", "/api/users", "/api/export"],
            "requests_per_minute": 30,
            "burst": 5,
        },
    }
    return config


def main():
    parser = argparse.ArgumentParser(description="API Abuse Detection Agent")
    parser.add_argument("--log", required=True, help="API access log (JSON lines)")
    parser.add_argument("--output", default="api_abuse_report.json")
    parser.add_argument("--action", choices=[
        "brute_force", "scraping", "stuffing", "bypass", "config", "full_analysis"
    ], default="full_analysis")
    args = parser.parse_args()

    logs = load_access_logs(args.log)
    report = {"generated_at": datetime.utcnow().isoformat(), "total_requests": len(logs),
              "findings": {}}
    print(f"[+] Loaded {len(logs)} API requests")

    if args.action in ("brute_force", "full_analysis"):
        f = detect_brute_force(logs)
        report["findings"]["brute_force"] = f
        print(f"[+] Brute force sources: {len(f)}")

    if args.action in ("scraping", "full_analysis"):
        f = detect_api_scraping(logs)
        report["findings"]["api_scraping"] = f
        print(f"[+] Scraping sources: {len(f)}")

    if args.action in ("stuffing", "full_analysis"):
        f = detect_credential_stuffing(logs)
        report["findings"]["credential_stuffing"] = f
        print(f"[+] Credential stuffing sources: {len(f)}")

    if args.action in ("bypass", "full_analysis"):
        f = detect_rate_limit_bypass(logs)
        report["findings"]["bypass_attempts"] = f
        print(f"[+] Rate limit bypass attempts: {len(f)}")

    if args.action in ("config", "full_analysis"):
        config = generate_rate_limit_config(logs)
        report["findings"]["recommended_config"] = config
        print("[+] Rate limit config generated")

    with open(args.output, "w") as fout:
        json.dump(report, fout, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
