#!/usr/bin/env python3
"""Agent for detecting service account abuse in Active Directory and cloud environments."""

import argparse
import json
import subprocess
from datetime import datetime, timezone


def query_ad_service_accounts():
    """Query Active Directory for service accounts via PowerShell."""
    ps_cmd = (
        "Get-ADUser -Filter {ServicePrincipalName -ne '$null'} "
        "-Properties ServicePrincipalName,LastLogonDate,Enabled,PasswordLastSet,"
        "PasswordNeverExpires,AdminCount,MemberOf "
        "| Select-Object SamAccountName,Enabled,LastLogonDate,PasswordLastSet,"
        "PasswordNeverExpires,AdminCount,@{N='SPNCount';E={$_.ServicePrincipalName.Count}} "
        "| ConvertTo-Json"
    )
    try:
        result = subprocess.check_output(
            ["powershell", "-NoProfile", "-Command", ps_cmd],
            text=True, errors="replace", timeout=30
        )
        data = json.loads(result) if result.strip().startswith(("[", "{")) else []
        return data if isinstance(data, list) else [data]
    except (subprocess.SubprocessError, json.JSONDecodeError):
        return []


def check_interactive_logons(days=7):
    """Find service accounts with interactive logon events (type 2/10)."""
    ps_cmd = (
        f"Get-WinEvent -FilterHashtable @{{LogName='Security';Id=4624;"
        f"StartTime=(Get-Date).AddDays(-{days})}} "
        "| Where-Object {($_.Properties[8].Value -eq 2 -or $_.Properties[8].Value -eq 10) "
        "-and $_.Properties[5].Value -match 'svc_|service'} "
        "| Select-Object TimeCreated,@{N='Account';E={$_.Properties[5].Value}},"
        "@{N='LogonType';E={$_.Properties[8].Value}},"
        "@{N='SourceIP';E={$_.Properties[18].Value}} "
        "| ConvertTo-Json"
    )
    try:
        result = subprocess.check_output(
            ["powershell", "-NoProfile", "-Command", ps_cmd],
            text=True, errors="replace", timeout=30
        )
        data = json.loads(result) if result.strip() else []
        return data if isinstance(data, list) else [data]
    except (subprocess.SubprocessError, json.JSONDecodeError):
        return []


def analyze_logon_patterns(events):
    """Detect anomalous logon patterns for service accounts."""
    anomalies = []
    account_sources = {}
    for evt in events:
        acct = evt.get("Account", "unknown")
        src = evt.get("SourceIP", "unknown")
        account_sources.setdefault(acct, []).append(src)

    for acct, sources in account_sources.items():
        unique = set(sources)
        if len(unique) > 3:
            anomalies.append({
                "account": acct,
                "issue": "Service account logged in from multiple sources",
                "source_count": len(unique),
                "sources": list(unique)[:10],
            })
    return anomalies


def check_account_risks(accounts):
    """Identify risky service account configurations."""
    risks = []
    for acct in accounts:
        name = acct.get("SamAccountName", "unknown")
        issues = []
        if acct.get("PasswordNeverExpires"):
            issues.append("Password never expires")
        if acct.get("AdminCount") == 1:
            issues.append("Has AdminCount=1 (privileged)")
        if acct.get("Enabled") is False:
            issues.append("Account disabled but has SPNs")
        pw_set = acct.get("PasswordLastSet")
        if pw_set and isinstance(pw_set, str):
            try:
                pw_date = datetime.fromisoformat(pw_set.replace("Z", "+00:00"))
                age = (datetime.now(timezone.utc) - pw_date).days
                if age > 365:
                    issues.append(f"Password {age} days old")
            except ValueError:
                pass
        if issues:
            risks.append({"account": name, "issues": issues, "risk_count": len(issues)})
    return risks


def main():
    parser = argparse.ArgumentParser(
        description="Detect service account abuse in AD environments"
    )
    parser.add_argument("--days", type=int, default=7, help="Lookback days for logon events")
    parser.add_argument("--output", "-o", help="Output JSON report path")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    print("[*] Service Account Abuse Detection Agent")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}

    accounts = query_ad_service_accounts()
    report["findings"]["service_accounts"] = len(accounts)
    print(f"[*] Found {len(accounts)} service accounts with SPNs")

    risks = check_account_risks(accounts)
    report["findings"]["risky_accounts"] = risks
    print(f"[*] Risky accounts: {len(risks)}")

    logons = check_interactive_logons(args.days)
    report["findings"]["interactive_logons"] = len(logons)
    anomalies = analyze_logon_patterns(logons)
    report["findings"]["logon_anomalies"] = anomalies
    print(f"[*] Logon anomalies: {len(anomalies)}")

    total_issues = len(risks) + len(anomalies)
    report["risk_level"] = "CRITICAL" if total_issues >= 5 else "HIGH" if total_issues >= 3 else "MEDIUM" if total_issues > 0 else "LOW"

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
