#!/usr/bin/env python3
"""Agent for conducting access review and certification using identity governance APIs."""

import json
import argparse
import csv
from datetime import datetime, timezone, timedelta


def load_access_data(csv_path):
    """Load access entitlement data from CSV export."""
    with open(csv_path) as f:
        reader = csv.DictReader(f)
        data = list(reader)
    print(f"[*] Loaded {len(data)} entitlement records from {csv_path}")
    return data


def identify_orphaned_accounts(records):
    """Find accounts with no manager or terminated status."""
    findings = []
    for r in records:
        if not r.get("manager") or r.get("status", "").lower() == "terminated":
            findings.append({"user": r.get("username"), "status": r.get("status"),
                             "manager": r.get("manager", "NONE"), "severity": "HIGH",
                             "issue": "Orphaned/terminated account with active access"})
    print(f"\n[*] Orphaned/terminated accounts: {len(findings)}")
    for f in findings[:10]:
        print(f"  [!] {f['user']} (status={f['status']}, manager={f['manager']})")
    return findings


def check_sod_violations(records, sod_rules):
    """Check for separation of duties violations."""
    user_entitlements = {}
    for r in records:
        user = r.get("username", "")
        ent = r.get("entitlement", "")
        user_entitlements.setdefault(user, set()).add(ent)
    findings = []
    for user, ents in user_entitlements.items():
        for rule in sod_rules:
            if rule["role_a"] in ents and rule["role_b"] in ents:
                findings.append({"user": user, "conflict": f"{rule['role_a']} + {rule['role_b']}",
                                 "severity": "CRITICAL", "rule": rule.get("name", "")})
    print(f"\n[*] SoD violations: {len(findings)}")
    for f in findings[:10]:
        print(f"  [!] {f['user']}: {f['conflict']}")
    return findings


def identify_excessive_access(records, threshold=10):
    """Find users with entitlement counts above threshold."""
    user_counts = {}
    for r in records:
        user = r.get("username", "")
        user_counts[user] = user_counts.get(user, 0) + 1
    excessive = [{"user": u, "count": c, "severity": "MEDIUM"}
                 for u, c in user_counts.items() if c > threshold]
    excessive.sort(key=lambda x: -x["count"])
    print(f"\n[*] Users with >{threshold} entitlements: {len(excessive)}")
    for e in excessive[:10]:
        print(f"  [!] {e['user']}: {e['count']} entitlements")
    return excessive


def check_last_used(records, stale_days=90):
    """Find entitlements not used within the stale period."""
    cutoff = datetime.now(timezone.utc) - timedelta(days=stale_days)
    stale = []
    for r in records:
        last_used = r.get("last_used", "")
        if last_used:
            try:
                lu_dt = datetime.fromisoformat(last_used.replace("Z", "+00:00"))
                if lu_dt < cutoff:
                    stale.append({"user": r.get("username"), "entitlement": r.get("entitlement"),
                                  "last_used": last_used, "severity": "MEDIUM"})
            except ValueError:
                pass
    print(f"\n[*] Stale entitlements (>{stale_days} days unused): {len(stale)}")
    return stale


def generate_report(orphaned, sod, excessive, stale, output_path):
    """Generate access review report."""
    report = {"review_date": datetime.now(timezone.utc).isoformat(),
              "summary": {"orphaned_accounts": len(orphaned), "sod_violations": len(sod),
                          "excessive_access": len(excessive), "stale_entitlements": len(stale)},
              "orphaned": orphaned, "sod_violations": sod,
              "excessive_access": excessive[:50], "stale_entitlements": stale[:50]}
    with open(output_path, "w") as f:
        json.dump(report, f, indent=2, default=str)
    total = len(orphaned) + len(sod) + len(excessive) + len(stale)
    print(f"\n[*] Report saved to {output_path} | Total findings: {total}")


def main():
    parser = argparse.ArgumentParser(description="Access Review and Certification Agent")
    parser.add_argument("action", choices=["orphaned", "sod", "excessive", "stale", "full-review"])
    parser.add_argument("--data", required=True, help="CSV file with access entitlements")
    parser.add_argument("--sod-rules", help="JSON file with SoD rules")
    parser.add_argument("--threshold", type=int, default=10, help="Excessive access threshold")
    parser.add_argument("--stale-days", type=int, default=90)
    parser.add_argument("-o", "--output", default="access_review.json")
    args = parser.parse_args()

    records = load_access_data(args.data)
    sod_rules = []
    if args.sod_rules:
        with open(args.sod_rules) as f:
            sod_rules = json.load(f)

    if args.action == "orphaned":
        identify_orphaned_accounts(records)
    elif args.action == "sod":
        check_sod_violations(records, sod_rules)
    elif args.action == "excessive":
        identify_excessive_access(records, args.threshold)
    elif args.action == "stale":
        check_last_used(records, args.stale_days)
    elif args.action == "full-review":
        o = identify_orphaned_accounts(records)
        s = check_sod_violations(records, sod_rules)
        e = identify_excessive_access(records, args.threshold)
        st = check_last_used(records, args.stale_days)
        generate_report(o, s, e, st, args.output)


if __name__ == "__main__":
    main()
