import subprocess, json, urllib.parse
from datetime import datetime, timedelta

TENANT = '5c53ae9f-7071-4248-b834-8685b646450f'
APP = 'fabb3421-8b34-484b-bc17-e46de9703418'
SECRET = '~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO'
BILLING_ID = '4f708b80-e537-4f63-92d3-5feedfa28244'
BILLING_UPN = 'billing@valleywideplastering.com'
ATTACKER_IPS = {'23.234.100.200', '23.234.100.73', '23.234.101.73'}

def get_token():
    r = subprocess.run(['curl','-s','-X','POST',
        f'https://login.microsoftonline.com/{TENANT}/oauth2/v2.0/token',
        '-d', f'client_id={APP}&client_secret={SECRET}&scope=https://graph.microsoft.com/.default&grant_type=client_credentials'],
        capture_output=True, text=True)
    return json.loads(r.stdout)['access_token']

def graph_get(token, url):
    r = subprocess.run(['curl','-s','-H',f'Authorization: Bearer {token}', url],
        capture_output=True, text=True)
    try:
        return json.loads(r.stdout)
    except:
        return {'error': {'message': r.stdout[:300] if r.stdout else 'empty'}}

token = get_token()
print('[OK] Token acquired')

# 1. INBOX RULES
print('\n' + '=' * 60)
print('1. INBOX RULES')
print('=' * 60)
rules = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders/inbox/messageRules')
if rules.get('value'):
    for r in rules.get('value', []):
        enabled = '[ENABLED]' if r.get('isEnabled') else '[DISABLED]'
        name = r.get('displayName', '?')
        rid = r.get('id', '')
        print(f'  {enabled} Rule: "{name}"')
        if r.get('conditions'):
            print(f'    Conditions: {json.dumps(r["conditions"], indent=6)}')
        if r.get('actions'):
            print(f'    Actions: {json.dumps(r["actions"], indent=6)}')
        actions = r.get('actions', {})
        if actions.get('markAsRead') or actions.get('forwardTo') or actions.get('redirectTo'):
            print(f'    >>> [CRITICAL] Suspicious action!')
        if len(name) <= 2:
            print(f'    >>> [CRITICAL] Short name - possible attacker rule!')
        print()
else:
    print('  No inbox rules found')
    if 'error' in rules:
        print(f'  Error: {rules["error"].get("message", "")[:200]}')

# 2. SIGN-IN LOGS
print('\n' + '=' * 60)
print('2. SIGN-IN LOGS (Last 30 days)')
print('=' * 60)
token = get_token()
month_ago = (datetime.utcnow() - timedelta(days=30)).strftime('%Y-%m-%dT00:00:00Z')
filter_str = urllib.parse.quote(f"userPrincipalName eq '{BILLING_UPN}' and createdDateTime ge {month_ago}")
signins = graph_get(token, f'https://graph.microsoft.com/v1.0/auditLogs/signIns?%24filter={filter_str}&%24top=100&%24orderby=createdDateTime%20desc')
if 'error' in signins:
    print(f'  v1.0 Error: {signins["error"].get("message", "")[:200]}')
    signins = graph_get(token, f'https://graph.microsoft.com/beta/auditLogs/signIns?%24filter={filter_str}&%24top=100&%24orderby=createdDateTime%20desc')
    if 'error' in signins:
        print(f'  Beta error: {signins["error"].get("message", "")[:200]}')

entries = signins.get('value', [])
print(f'  Total entries: {len(entries)}')
ips_seen = {}
for s in entries:
    dt = s.get('createdDateTime', '')
    ip = s.get('ipAddress', '?')
    loc = s.get('location', {})
    city = loc.get('city', '?')
    country = loc.get('countryOrRegion', '?')
    app = s.get('clientAppUsed', '?')
    resource = s.get('resourceDisplayName', '?')
    status = s.get('status', {})
    err = status.get('errorCode', 0)
    risk = s.get('riskLevelDuringSignIn', 'none')
    is_attacker = ip in ATTACKER_IPS
    is_foreign = country not in ('US', '?', '')
    flag = '[CRITICAL]' if is_attacker else ('[SUSPICIOUS]' if is_foreign or (risk and risk != 'none') else '          ')
    print(f'  {flag} {dt} | {ip} | {city}, {country} | {app} | {resource} | err={err} risk={risk}')
    if ip not in ips_seen:
        ips_seen[ip] = {'city': city, 'country': country, 'count': 0, 'apps': set(), 'failed': 0, 'attacker': is_attacker}
    ips_seen[ip]['count'] += 1
    ips_seen[ip]['apps'].add(app)
    if err != 0:
        ips_seen[ip]['failed'] += 1

print(f'\n  Unique IPs:')
for ip, info in sorted(ips_seen.items(), key=lambda x: -x[1]['count']):
    flag = '[CRITICAL]' if info['attacker'] else ('[SUSPICIOUS]' if info['country'] not in ('US', '?', '') else '          ')
    print(f'    {flag} {ip} | {info["city"]}, {info["country"]} | {info["count"]}x ({info["failed"]} failed) | Apps: {", ".join(info["apps"])}')

# 3. SENT MAIL
print('\n' + '=' * 60)
print('3. SENT MAIL (Last 100)')
print('=' * 60)
token = get_token()
sent = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders/SentItems/messages?$top=100&$orderby=sentDateTime%20desc&$select=subject,toRecipients,sentDateTime,bodyPreview,hasAttachments')
suspicious_words = ['invoice', 'payment', 'urgent', 'wire', 'transfer', 'docusign', 'password', 'verify', 'confirm', 'bank', 'account', 'shared', 'document', 'sign']
susp_count = 0
for m in sent.get('value', []):
    dt = m.get('sentDateTime', '')
    subj = m.get('subject', '(no subject)')
    to_list = [r.get('emailAddress', {}).get('address', '') for r in m.get('toRecipients', [])]
    attach = ' [ATTACH]' if m.get('hasAttachments') else ''
    is_suspicious = any(w in (subj or '').lower() for w in suspicious_words)
    flag = '[SUSPICIOUS]' if is_suspicious else '            '
    if is_suspicious:
        susp_count += 1
    print(f'  {flag} {dt} | To: {", ".join(to_list[:3])} | {subj}{attach}')
    if is_suspicious:
        preview = m.get("bodyPreview", "")[:200].encode('ascii', 'replace').decode()
        print(f'    Preview: {preview}')
print(f'\n  Total sent: {len(sent.get("value", []))} | Flagged: {susp_count}')

# 4. AUTH METHODS
print('\n' + '=' * 60)
print('4. AUTHENTICATION METHODS')
print('=' * 60)
auth = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/authentication/methods')
for m in auth.get('value', []):
    mtype = m.get('@odata.type', '').replace('#microsoft.graph.', '')
    name = m.get('displayName', '')
    mid = m.get('id', '')
    created = m.get('createdDateTime', 'unknown')
    print(f'  {mtype} | {name} | ID: {mid} | Created: {created}')
    if 'phoneNumber' in m:
        print(f'    Phone: {m["phoneNumber"]}')

# 5. MAILBOX SETTINGS
print('\n' + '=' * 60)
print('5. MAILBOX SETTINGS')
print('=' * 60)
settings = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailboxSettings')
auto = settings.get('automaticRepliesSetting', {})
print(f'  Auto-replies: {auto.get("status", "unknown")}')
if auto.get('status') != 'disabled':
    print(f'  [SUSPICIOUS] Internal: {auto.get("internalReplyMessage", "")[:200]}')
    print(f'  [SUSPICIOUS] External: {auto.get("externalReplyMessage", "")[:200]}')
fwd = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}?$select=mail,otherMails,proxyAddresses')
print(f'  Mail: {fwd.get("mail")}')
print(f'  Other mails: {fwd.get("otherMails", [])}')
print(f'  Proxy addresses: {fwd.get("proxyAddresses", [])}')

# 6. MAIL FOLDERS
print('\n' + '=' * 60)
print('6. MAIL FOLDERS')
print('=' * 60)
token = get_token()
folders = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders?$top=50&$select=displayName,totalItemCount,unreadItemCount,id')
archive_id = None
for f in folders.get('value', []):
    count = f.get('totalItemCount', 0)
    unread = f.get('unreadItemCount', 0)
    name = f.get('displayName', '?')
    flag = '[CHECK]' if name == 'Archive' and count > 0 else '       '
    if name == 'Archive' and count > 0:
        archive_id = f.get('id')
    if count > 0:
        print(f'  {flag} {name}: {count} items ({unread} unread)')

# Check child folders
print('\n  Child folders:')
for f in folders.get('value', []):
    fid = f.get('id', '')
    fname = f.get('displayName', '')
    children = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders/{fid}/childFolders?$select=displayName,totalItemCount,id')
    for child in children.get('value', []):
        cname = child.get('displayName', '?')
        ccount = child.get('totalItemCount', 0)
        if ccount > 0:
            print(f'    {fname}/{cname}: {ccount} items')

# 7. OAUTH GRANTS
print('\n' + '=' * 60)
print('7. OAUTH PERMISSION GRANTS')
print('=' * 60)
grants = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/oauth2PermissionGrants')
if grants.get('value'):
    for g in grants['value']:
        print(f'  [SUSPICIOUS] Client: {g.get("clientId")} | Scope: {g.get("scope")} | ConsentType: {g.get("consentType")}')
else:
    print('  [OK] No OAuth grants found')

# 8. RECENT INBOX
print('\n' + '=' * 60)
print('8. RECENT INBOX (Last 7 days)')
print('=' * 60)
token = get_token()
week_ago = (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%dT00:00:00Z')
inbox = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders/inbox/messages?$top=50&$orderby=receivedDateTime%20desc&$select=subject,from,receivedDateTime,hasAttachments,isRead&$filter=receivedDateTime%20ge%20{week_ago}')
box_count = 0
for m in inbox.get('value', []):
    dt = m.get('receivedDateTime', '')
    subj = m.get('subject', '(no subject)')
    sender = m.get('from', {}).get('emailAddress', {}).get('address', '?')
    attach = ' [ATTACH]' if m.get('hasAttachments') else ''
    is_box = 'box.com' in (subj or '').lower() or 'box.com' in sender.lower()
    is_reset = 'password' in (subj or '').lower() or 'reset' in (subj or '').lower()
    flag = '[CHECK]' if is_box or is_reset else '       '
    if is_box: box_count += 1
    print(f'  {flag} {dt} | From: {sender} | {subj}{attach}')
print(f'\n  Box.com related: {box_count}')

# 9. DELETED ITEMS
print('\n' + '=' * 60)
print('9. DELETED ITEMS (Last 50)')
print('=' * 60)
deleted = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders/deleteditems/messages?$top=50&$orderby=receivedDateTime%20desc&$select=subject,from,receivedDateTime,hasAttachments')
for m in deleted.get('value', []):
    dt = m.get('receivedDateTime', '')
    subj = m.get('subject', '(no subject)')
    sender = m.get('from', {}).get('emailAddress', {}).get('address', '?')
    attach = ' [ATTACH]' if m.get('hasAttachments') else ''
    is_box = 'box.com' in (subj or '').lower() or 'box.com' in sender.lower()
    flag = '[CHECK]' if is_box else '       '
    print(f'  {flag} {dt} | From: {sender} | {subj}{attach}')

# 10. ARCHIVE
print('\n' + '=' * 60)
print('10. ARCHIVE FOLDER')
print('=' * 60)
if archive_id:
    archive_msgs = graph_get(token, f'https://graph.microsoft.com/v1.0/users/{BILLING_ID}/mailFolders/{archive_id}/messages?$top=50&$orderby=receivedDateTime%20desc&$select=subject,from,receivedDateTime,isRead')
    items = archive_msgs.get('value', [])
    print(f'  Archive items found: {len(items)}')
    for m in items:
        dt = m.get('receivedDateTime', '')
        subj = m.get('subject', '(no subject)')
        sender = m.get('from', {}).get('emailAddress', {}).get('address', '?')
        read_status = '' if m.get('isRead') else ' [UNREAD]'
        print(f'  {dt} | From: {sender} | {subj}{read_status}')
else:
    print('  [OK] No items in Archive')

print('\n' + '=' * 60)
print('[DONE] Billing account deep check complete')
print('=' * 60)
