"""
VWP BEC Investigation - Exchange Email Trace
Find recipient email addresses that received phishing Box.com links from JR's account.
"""

import subprocess
import json
import re
import sys
from datetime import datetime

# Credentials
TENANT_ID = "5c53ae9f-7071-4248-b834-8685b646450f"
APP_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
JR_ID = "0af923d0-48c5-4cc1-8553-c60625802815"

GRAPH_BASE = "https://graph.microsoft.com/v1.0"

def get_token():
    """Get OAuth token via client credentials flow."""
    url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
    result = subprocess.run([
        "curl", "-s", "-X", "POST", url,
        "-H", "Content-Type: application/x-www-form-urlencoded",
        "-d", f"client_id={APP_ID}&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret={SECRET}&grant_type=client_credentials"
    ], capture_output=True, text=True)
    data = json.loads(result.stdout)
    if "access_token" not in data:
        print(f"[ERROR] Token acquisition failed: {json.dumps(data, indent=2)}")
        sys.exit(1)
    print("[OK] Token acquired successfully")
    return data["access_token"]


def graph_get(token, url):
    """Make a GET request to Graph API."""
    result = subprocess.run([
        "curl", "-s", "-X", "GET", url,
        "-H", f"Authorization: Bearer {token}",
        "-H", "Content-Type: application/json"
    ], capture_output=True, text=True)
    try:
        return json.loads(result.stdout)
    except json.JSONDecodeError:
        print(f"[ERROR] Failed to parse response from {url}")
        print(f"  Raw: {result.stdout[:500]}")
        return None


def extract_recipients(msg):
    """Extract all recipient email addresses from a message."""
    recipients = set()
    for field in ["toRecipients", "ccRecipients", "bccRecipients"]:
        for r in msg.get(field, []) or []:
            email = r.get("emailAddress", {}).get("address", "")
            if email:
                recipients.add(email.lower())
    return recipients


def extract_emails_from_html(html_text):
    """Extract email addresses from HTML body content."""
    if not html_text:
        return set()
    # Standard email regex
    pattern = r'[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}'
    found = set(e.lower() for e in re.findall(pattern, html_text))
    # Filter out common system/noreply addresses
    system_addrs = {"noreply@box.com", "no-reply@box.com", "support@box.com"}
    return found - system_addrs


def approach1_search_sent_box(token):
    """Search JR's sent mail for messages containing box.com links."""
    print("\n" + "="*70)
    print("APPROACH 1: Search JR's Sent Items for Box.com links")
    print("="*70)

    all_messages = []
    all_recipients = set()

    # Search 1: "box.com" in all mail
    url = (f"{GRAPH_BASE}/users/{JR_ID}/messages"
           f"?$search=%22box.com%22"
           f"&$top=100"
           f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,sender,parentFolderId")

    print("\n[INFO] Searching all mail for 'box.com'...")
    data = graph_get(token, url)
    if data and "value" in data:
        msgs = data["value"]
        print(f"  Found {len(msgs)} messages matching 'box.com'")
        for msg in msgs:
            sender = msg.get("from", {}).get("emailAddress", {}).get("address", "unknown")
            subj = msg.get("subject", "(no subject)")
            sent = msg.get("sentDateTime", "")
            recips = extract_recipients(msg)
            all_recipients.update(recips)
            all_messages.append({
                "subject": subj,
                "sender": sender,
                "sentDateTime": sent,
                "recipients": list(recips),
                "source": "approach1_box_search"
            })
            recip_str = ", ".join(recips) if recips else "(none visible)"
            print(f"  [{sent[:10] if sent else '??'}] From: {sender}")
            print(f"    Subject: {subj[:80]}")
            print(f"    To: {recip_str}")

        # Check for pagination
        next_link = data.get("@odata.nextLink")
        page = 2
        while next_link and page <= 5:
            print(f"  Fetching page {page}...")
            data = graph_get(token, next_link)
            if data and "value" in data:
                for msg in data["value"]:
                    sender = msg.get("from", {}).get("emailAddress", {}).get("address", "unknown")
                    subj = msg.get("subject", "(no subject)")
                    sent = msg.get("sentDateTime", "")
                    recips = extract_recipients(msg)
                    all_recipients.update(recips)
                    all_messages.append({
                        "subject": subj,
                        "sender": sender,
                        "sentDateTime": sent,
                        "recipients": list(recips),
                        "source": "approach1_box_search_page" + str(page)
                    })
                    recip_str = ", ".join(recips) if recips else "(none visible)"
                    print(f"  [{sent[:10] if sent else '??'}] From: {sender}")
                    print(f"    Subject: {subj[:80]}")
                    print(f"    To: {recip_str}")
                next_link = data.get("@odata.nextLink")
            else:
                break
            page += 1
    else:
        print(f"  [WARNING] No results or error: {json.dumps(data, indent=2)[:300] if data else 'None'}")

    # Search 2: "Valley Wide Plastering" in all mail
    url2 = (f"{GRAPH_BASE}/users/{JR_ID}/messages"
            f"?$search=%22Valley+Wide+Plastering%22"
            f"&$top=100"
            f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,sender")

    print("\n[INFO] Searching all mail for 'Valley Wide Plastering'...")
    data2 = graph_get(token, url2)
    if data2 and "value" in data2:
        msgs = data2["value"]
        print(f"  Found {len(msgs)} messages matching 'Valley Wide Plastering'")
        for msg in msgs:
            sender = msg.get("from", {}).get("emailAddress", {}).get("address", "unknown")
            subj = msg.get("subject", "(no subject)")
            sent = msg.get("sentDateTime", "")
            recips = extract_recipients(msg)
            all_recipients.update(recips)
            # Avoid duplicates in message list
            all_messages.append({
                "subject": subj,
                "sender": sender,
                "sentDateTime": sent,
                "recipients": list(recips),
                "source": "approach1_vwp_search"
            })
            recip_str = ", ".join(recips) if recips else "(none visible)"
            print(f"  [{sent[:10] if sent else '??'}] From: {sender}")
            print(f"    Subject: {subj[:80]}")
            print(f"    To: {recip_str}")
    else:
        print(f"  [WARNING] No results or error: {json.dumps(data2, indent=2)[:300] if data2 else 'None'}")

    print(f"\n[INFO] Approach 1 unique recipients: {len(all_recipients)}")
    return all_messages, all_recipients


def approach2_box_notifications(token):
    """Get full HTML body of Box acceptance notification emails."""
    print("\n" + "="*70)
    print("APPROACH 2: Full HTML body of Box acceptance notifications")
    print("="*70)

    all_recipients = set()
    all_messages = []

    # Search for noreply@box.com messages
    url = (f"{GRAPH_BASE}/users/{JR_ID}/messages"
           f"?$search=%22from%3Anoreply%40box.com%22"
           f"&$top=10"
           f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,body")

    print("\n[INFO] Searching for emails from noreply@box.com (with full body)...")
    data = graph_get(token, url)
    if data and "value" in data:
        msgs = data["value"]
        print(f"  Found {len(msgs)} messages from Box notifications")
        for i, msg in enumerate(msgs[:5]):
            subj = msg.get("subject", "(no subject)")
            sent = msg.get("sentDateTime", "")
            body = msg.get("body", {})
            body_content = body.get("content", "") if body else ""

            print(f"\n  --- Notification {i+1} ---")
            print(f"  Subject: {subj[:80]}")
            print(f"  Date: {sent}")

            # Extract emails from HTML body
            emails_found = extract_emails_from_html(body_content)
            recips = extract_recipients(msg)
            all_recipients.update(recips)
            all_recipients.update(emails_found)

            if emails_found:
                print(f"  Emails in body: {', '.join(emails_found)}")
            else:
                print(f"  No extra emails found in HTML body")

            if recips:
                print(f"  Header recipients: {', '.join(recips)}")

            # Show a snippet of body for debugging
            if body_content:
                # Strip HTML tags for preview
                text_preview = re.sub(r'<[^>]+>', ' ', body_content)
                text_preview = re.sub(r'\s+', ' ', text_preview).strip()
                print(f"  Body preview: {text_preview[:200]}...")

            all_messages.append({
                "subject": subj,
                "sentDateTime": sent,
                "emails_in_body": list(emails_found),
                "header_recipients": list(recips),
                "source": "approach2_box_notification"
            })
    else:
        print(f"  [WARNING] No results or error: {json.dumps(data, indent=2)[:300] if data else 'None'}")

    # Also try searching for "accepted" from box.com
    url2 = (f"{GRAPH_BASE}/users/{JR_ID}/messages"
            f"?$search=%22accepted%22+%22box.com%22"
            f"&$top=10"
            f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,body")

    print("\n[INFO] Searching for 'accepted' + 'box.com' messages (with full body)...")
    data2 = graph_get(token, url2)
    if data2 and "value" in data2:
        msgs = data2["value"]
        print(f"  Found {len(msgs)} messages")
        for i, msg in enumerate(msgs[:5]):
            subj = msg.get("subject", "(no subject)")
            sent = msg.get("sentDateTime", "")
            body = msg.get("body", {})
            body_content = body.get("content", "") if body else ""

            print(f"\n  --- Message {i+1} ---")
            print(f"  Subject: {subj[:80]}")
            print(f"  Date: {sent}")

            emails_found = extract_emails_from_html(body_content)
            recips = extract_recipients(msg)
            all_recipients.update(recips)
            all_recipients.update(emails_found)

            if emails_found:
                print(f"  Emails in body: {', '.join(emails_found)}")
            if recips:
                print(f"  Header recipients: {', '.join(recips)}")

            all_messages.append({
                "subject": subj,
                "sentDateTime": sent,
                "emails_in_body": list(emails_found),
                "header_recipients": list(recips),
                "source": "approach2_accepted_search"
            })
    else:
        print(f"  [WARNING] No results or error")

    print(f"\n[INFO] Approach 2 unique recipients: {len(all_recipients)}")
    return all_messages, all_recipients


def approach3_box_invitations(token):
    """Search for Box invitation emails (invited you / shared)."""
    print("\n" + "="*70)
    print("APPROACH 3: Search for Box invitation/sharing emails")
    print("="*70)

    all_recipients = set()
    all_messages = []

    searches = [
        ("invited you", f"{GRAPH_BASE}/users/{JR_ID}/messages"
         f"?$search=%22invited+you%22"
         f"&$top=50"
         f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,sender,body"),
        ("shared with you box", f"{GRAPH_BASE}/users/{JR_ID}/messages"
         f"?$search=%22shared%22+%22box.com%22"
         f"&$top=50"
         f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,sender,body"),
        ("invitation box", f"{GRAPH_BASE}/users/{JR_ID}/messages"
         f"?$search=%22invitation%22+%22box%22"
         f"&$top=50"
         f"&$select=subject,toRecipients,ccRecipients,bccRecipients,sentDateTime,from,sender,body"),
    ]

    for label, url in searches:
        print(f"\n[INFO] Searching for '{label}'...")
        data = graph_get(token, url)
        if data and "value" in data:
            msgs = data["value"]
            print(f"  Found {len(msgs)} messages")
            for msg in msgs:
                sender = msg.get("from", {}).get("emailAddress", {}).get("address", "unknown")
                subj = msg.get("subject", "(no subject)")
                sent = msg.get("sentDateTime", "")
                body = msg.get("body", {})
                body_content = body.get("content", "") if body else ""

                recips = extract_recipients(msg)
                emails_in_body = extract_emails_from_html(body_content)
                all_recipients.update(recips)
                all_recipients.update(emails_in_body)

                recip_str = ", ".join(recips) if recips else "(none)"
                body_emails_str = ", ".join(emails_in_body) if emails_in_body else "(none)"

                try:
                    safe_subj = subj[:80].encode('ascii', errors='replace').decode('ascii')
                    print(f"  [{sent[:10] if sent else '??'}] From: {sender}")
                    print(f"    Subject: {safe_subj}")
                    print(f"    Header recipients: {recip_str}")
                    if emails_in_body:
                        print(f"    Body emails: {body_emails_str}")
                except Exception:
                    pass

                all_messages.append({
                    "subject": subj[:80],
                    "sender": sender,
                    "sentDateTime": sent,
                    "recipients": list(recips),
                    "emails_in_body": list(emails_in_body),
                    "source": f"approach3_{label.replace(' ', '_')}"
                })
        else:
            print(f"  [WARNING] No results or error")

    print(f"\n[INFO] Approach 3 unique recipients: {len(all_recipients)}")
    return all_messages, all_recipients


def main():
    print("VWP BEC Investigation - Exchange Email Trace")
    print(f"Timestamp: {datetime.now().isoformat()}")
    print(f"Target: JR ({JR_ID})")
    print("="*70)
    sys.stdout.flush()

    token = get_token()
    sys.stdout.flush()

    # Run all three approaches
    try:
        msgs1, recips1 = approach1_search_sent_box(token)
    except Exception as e:
        print(f"[ERROR] Approach 1 failed: {e}")
        msgs1, recips1 = [], set()
    sys.stdout.flush()

    try:
        msgs2, recips2 = approach2_box_notifications(token)
    except Exception as e:
        print(f"[ERROR] Approach 2 failed: {e}")
        msgs2, recips2 = [], set()
    sys.stdout.flush()

    try:
        msgs3, recips3 = approach3_box_invitations(token)
    except Exception as e:
        print(f"[ERROR] Approach 3 failed: {e}")
        msgs3, recips3 = [], set()
    sys.stdout.flush()

    # Combine all results
    all_recipients = recips1 | recips2 | recips3
    all_messages = msgs1 + msgs2 + msgs3

    # Identify JR's own email addresses
    jr_emails = {"j-r@valleywideplastering.com"}
    for msg in all_messages:
        sender = msg.get("sender", "")
        if sender and "valleywideplastering" in sender.lower():
            jr_emails.add(sender.lower())

    # Filter out JR's own addresses and noreply
    system_addrs = {"noreply@box.com", "no-reply@box.com"}
    victim_recipients = all_recipients - jr_emails - system_addrs

    # Save results FIRST before printing summary
    output = {
        "investigation": "VWP BEC - Exchange Email Trace",
        "timestamp": datetime.now().isoformat(),
        "target_user_id": JR_ID,
        "jr_email_addresses": sorted(jr_emails),
        "all_unique_recipients": sorted(all_recipients),
        "victim_recipients_excluding_jr": sorted(victim_recipients),
        "total_messages_analyzed": len(all_messages),
        "approach1_count": len(msgs1),
        "approach2_count": len(msgs2),
        "approach3_count": len(msgs3),
        "messages": all_messages
    }

    output_path = r"D:\ClaudeTools\temp\vwp_exchange_recipients.json"
    with open(output_path, "w", encoding="utf-8") as f:
        json.dump(output, f, indent=2, ensure_ascii=False)

    # Now print summary
    print("\n" + "="*70)
    print("SUMMARY")
    print("="*70)
    print(f"Total messages found across all approaches: {len(all_messages)}")
    print(f"  Approach 1 (box.com search): {len(msgs1)} messages")
    print(f"  Approach 2 (notification bodies): {len(msgs2)} messages")
    print(f"  Approach 3 (invitation search): {len(msgs3)} messages")
    print(f"Total unique email addresses (all): {len(all_recipients)}")
    print(f"JR's own addresses: {jr_emails}")
    print(f"Unique victim addresses (excluding JR + system): {len(victim_recipients)}")

    print("\nAll unique victim email addresses:")
    for i, email in enumerate(sorted(victim_recipients), 1):
        print(f"  {i}. {email}")

    print(f"\n[OK] Results saved to {output_path}")
    print(f"[INFO] {len(victim_recipients)} unique victim recipient addresses identified")
    sys.stdout.flush()


if __name__ == "__main__":
    main()
