#!/usr/bin/env python3
"""Personal Radar — full-spectrum scan of all services, APIs, emails, calendar, and background services."""
import json, os, sys, subprocess, ssl, urllib.request, urllib.error, urllib.parse
from datetime import datetime, timedelta, timezone

WORKSPACE = os.environ.get('WORKSPACE', '/root/.openclaw/workspace')
GOOGLE_AUTH_DIR = os.path.join(WORKSPACE, 'google-auth')
SECRETS_DIR = '/home/clawd/secrets'

# Load env vars from bashrc if not already set (cron sessions don't source bashrc)
def _load_bashrc_env():
    bashrc = os.path.expanduser('~/.bashrc')
    if not os.path.exists(bashrc):
        return
    with open(bashrc) as f:
        for line in f:
            line = line.strip()
            if line.startswith('export ') and '=' in line:
                kv = line[7:]  # strip 'export '
                k, _, v = kv.partition('=')
                k = k.strip()
                v = v.strip().strip('"').strip("'")
                if k and v and k not in os.environ:
                    os.environ[k] = v

_load_bashrc_env()

try:
    from zoneinfo import ZoneInfo
    LISBON_TZ = ZoneInfo('Europe/Lisbon')
except ImportError:
    LISBON_TZ = timezone.utc

# ─── Google Accounts ─────────────────────────────────────────────

def check_google_account(token_path, label):
    result = {'label': label, 'token_path': token_path, 'gmail': None, 'calendar': None}

    if not os.path.exists(token_path):
        result['gmail'] = {'status': '❌', 'error': f'Token not found: {token_path}'}
        result['calendar'] = {'status': '❌', 'error': 'No token'}
        return result

    try:
        from google.oauth2.credentials import Credentials
        from googleapiclient.discovery import build
        from google.auth.transport.requests import Request

        with open(token_path) as f:
            d = json.load(f)

        creds = Credentials(
            token=d.get('access_token', d.get('token', '')),
            refresh_token=d.get('refresh_token'),
            token_uri='https://oauth2.googleapis.com/token',
            client_id=d['client_id'],
            client_secret=d['client_secret'],
        )
        creds.refresh(Request())

        d['access_token'] = creds.token
        d['token'] = creds.token
        with open(token_path, 'w') as f:
            json.dump(d, f)
    except Exception as e:
        result['gmail'] = {'status': '❌', 'error': f'Auth failed: {e}'}
        result['calendar'] = {'status': '❌', 'error': 'Auth failed'}
        return result

    # Gmail
    try:
        gmail = build('gmail', 'v1', credentials=creds)
        profile = gmail.users().getProfile(userId='me').execute()
        email = profile.get('emailAddress', '?')

        msgs = gmail.users().messages().list(
            userId='me', maxResults=15, labelIds=['INBOX'], q='is:unread'
        ).execute()

        messages = []
        for m in msgs.get('messages', [])[:10]:
            msg = gmail.users().messages().get(
                userId='me', id=m['id'], format='metadata',
                metadataHeaders=['From', 'Subject', 'Date']
            ).execute()
            headers = {h['name']: h['value'] for h in msg['payload']['headers']}
            messages.append({
                'id': m['id'],
                'from': headers.get('From', '?'),
                'subject': headers.get('Subject', '(no subject)'),
                'date': headers.get('Date', '?'),
                'snippet': msg.get('snippet', '')[:200]
            })

        result['gmail'] = {
            'status': '✅',
            'email': email,
            'unread_count': msgs.get('resultSizeEstimate', 0),
            'messages': messages
        }
    except Exception as e:
        result['gmail'] = {'status': '❌', 'error': str(e)}

    # Calendar
    try:
        cal = build('calendar', 'v3', credentials=creds)
        now = datetime.now(LISBON_TZ)
        today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
        tomorrow_end = today_start + timedelta(days=2)

        cal_list = cal.calendarList().list().execute()
        calendars = cal_list.get('items', [])

        all_events = []
        for c in calendars:
            try:
                events = cal.events().list(
                    calendarId=c['id'],
                    timeMin=today_start.isoformat(),
                    timeMax=tomorrow_end.isoformat(),
                    singleEvents=True, orderBy='startTime', maxResults=20
                ).execute()
                for ev in events.get('items', []):
                    ev['_calendar'] = c.get('summary', c['id'])
                    all_events.append(ev)
            except:
                pass

        def get_start(ev):
            s = ev.get('start', {})
            return s.get('dateTime', s.get('date', ''))
        all_events.sort(key=get_start)

        result['calendar'] = {
            'status': '✅',
            'calendar_count': len(calendars),
            'calendars': [{'name': c.get('summary', c['id']), 'access': c.get('accessRole', '?')} for c in calendars],
            'events': [{
                'summary': ev.get('summary', '(no title)'),
                'start': get_start(ev),
                'end': ev.get('end', {}).get('dateTime', ev.get('end', {}).get('date', '')),
                'calendar': ev.get('_calendar', ''),
                'location': ev.get('location', ''),
                'attendees': len(ev.get('attendees', []))
            } for ev in all_events]
        }
    except Exception as e:
        result['calendar'] = {'status': '❌', 'error': str(e)}

    return result

# ─── Service Checks ──────────────────────────────────────────────

def check_service(name, check_fn):
    try:
        result = check_fn()
        return {'name': name, 'status': '✅', **result}
    except Exception as e:
        return {'name': name, 'status': '❌', 'error': str(e)}


def check_notion():
    key = open(os.path.join(SECRETS_DIR, 'notion/api_key')).read().strip()
    req = urllib.request.Request(
        'https://api.notion.com/v1/users/me',
        headers={'Authorization': f'Bearer {key}', 'Notion-Version': '2022-06-28'}
    )
    data = json.loads(urllib.request.urlopen(req, timeout=10).read())
    return {'account': data.get('name', '?'), 'type': data.get('type', '?'), 'billing': 'Free (API included in workspace plan)'}


def check_figma():
    key = open(os.path.join(SECRETS_DIR, 'figma/api_token')).read().strip()
    req = urllib.request.Request('https://api.figma.com/v1/me', headers={'X-Figma-Token': key})
    data = json.loads(urllib.request.urlopen(req, timeout=10).read())
    return {'account': f"{data.get('handle', '?')} ({data.get('email', '?')})", 'billing': 'Free (included in Figma plan)'}


def check_github():
    r = subprocess.run(['gh', 'auth', 'status'], capture_output=True, text=True, timeout=10)
    out = r.stdout + r.stderr
    if 'Logged in' in out:
        return {'account': 'assafdagan20205', 'billing': 'Free (public repos unlimited)'}
    raise Exception(out.strip())


def check_gemini():
    key = os.environ.get('GEMINI_API_KEY', '')
    if not key:
        raise Exception('GEMINI_API_KEY not set in env')
    req = urllib.request.Request(f'https://generativelanguage.googleapis.com/v1beta/models?key={key}')
    data = json.loads(urllib.request.urlopen(req, timeout=10).read())
    models = data.get('models', [])
    image_models = [m['name'] for m in models if 'image' in m['name'].lower() or 'imagen' in m['name'].lower()]
    return {
        'total_models': len(models),
        'image_models': image_models,
        'billing': 'Paid plan (Google AI Studio). Free tier: 15 RPM flash / 2 RPM pro.',
        'key_prefix': key[:10] + '...'
    }


def check_recraft():
    key = os.environ.get('RECRAFT_API_KEY', '')
    if not key:
        raise Exception('RECRAFT_API_KEY not set in env')
    req = urllib.request.Request(
        'https://external.api.recraft.ai/v1/images/generations',
        data=json.dumps({'prompt': 'test', 'model': 'recraftv3'}).encode(),
        headers={'Authorization': f'Bearer {key}', 'Content-Type': 'application/json'},
        method='POST'
    )
    try:
        resp = urllib.request.urlopen(req, timeout=15)
        data = json.loads(resp.read())
        credits = data.get('credits', '?')
        return {'billing': f'Paid — {credits} credits remaining', 'credits': credits}
    except urllib.error.HTTPError as e:
        body = e.read().decode()[:200]
        if e.code == 401:
            raise Exception('401 Unauthorized — API key expired or revoked')
        elif e.code == 402:
            raise Exception('402 Payment required — credits depleted')
        elif e.code == 422:
            return {'billing': 'Paid — key valid (validation error on test)', 'key_prefix': key[:10] + '...'}
        raise Exception(f'HTTP {e.code}: {body}')


def check_xurl_account(app_name):
    """Switch xurl default, check whoami, switch back."""
    subprocess.run(['xurl', 'auth', 'default', app_name], capture_output=True, text=True, timeout=5)
    r = subprocess.run(['xurl', 'whoami'], capture_output=True, text=True, timeout=10)
    # Always restore default to assaf
    subprocess.run(['xurl', 'auth', 'default', 'assaf'], capture_output=True, text=True, timeout=5)
    try:
        data = json.loads(r.stdout)
        if 'data' in data:
            d = data['data']
            return {
                'account': f"@{d.get('username', '?')}",
                'name': d.get('name', '?'),
                'followers': d.get('public_metrics', {}).get('followers_count', 0),
                'tweets': d.get('public_metrics', {}).get('tweet_count', 0),
            }
        if data.get('status') == 401:
            raise Exception('401 Unauthorized')
        raise Exception(str(data))
    except json.JSONDecodeError:
        raise Exception(f'Bad response: {r.stdout[:100]}')


def check_xurl_assaf():
    result = check_xurl_account('assaf')
    result['billing'] = 'Free tier (Basic)'
    return result


def check_xurl_kitt():
    result = check_xurl_account('curious-endeavor')
    result['billing'] = 'Free tier (Basic)'
    return result


def check_linkedin_personal():
    token_path = os.path.join(GOOGLE_AUTH_DIR, 'linkedin-token.json')
    if not os.path.exists(token_path):
        raise Exception('Token not found: linkedin-token.json')
    token_data = json.load(open(token_path))
    token = token_data['access_token']

    # Try refresh if we have a refresh_token
    refresh_token = token_data.get('refresh_token')
    refreshed = False
    if refresh_token:
        try:
            client_id = token_data.get('client_id', '786j45b1v6ybir')
            client_secret = token_data.get('client_secret', 'WPL_AP1.8d1nw4Sn2S5yqfSC.jH3YYQ==')
            refresh_data = urllib.parse.urlencode({
                'grant_type': 'refresh_token',
                'refresh_token': refresh_token,
                'client_id': client_id,
                'client_secret': client_secret,
            }).encode()
            req = urllib.request.Request('https://www.linkedin.com/oauth/v2/accessToken', data=refresh_data)
            resp = json.loads(urllib.request.urlopen(req, timeout=10).read())
            if 'access_token' in resp:
                token_data['access_token'] = resp['access_token']
                if resp.get('refresh_token'):
                    token_data['refresh_token'] = resp['refresh_token']
                with open(token_path, 'w') as f:
                    json.dump(token_data, f, indent=2)
                token = resp['access_token']
                refreshed = True
        except Exception:
            pass  # Will fail on userinfo below if token is truly dead

    req = urllib.request.Request('https://api.linkedin.com/v2/userinfo', headers={'Authorization': f'Bearer {token}'})
    data = json.loads(urllib.request.urlopen(req, timeout=10).read())
    result = {
        'account': data.get('name', '?'),
        'email': data.get('email', '?'),
        'scopes': token_data.get('scope', 'openid, profile, email, w_member_social'),
        'billing': 'Free (personal posting)',
        'has_refresh_token': bool(refresh_token),
    }
    if refreshed:
        result['note'] = 'Token auto-refreshed ✅'
    elif not refresh_token:
        result['warning'] = '⚠️ No refresh token — will expire and need manual re-auth'
    return result


def check_linkedin_org():
    # Try to check if Community Management API is approved by testing org endpoint
    # First try with personal token (same app)
    token_path = os.path.join(GOOGLE_AUTH_DIR, 'linkedin-token.json')
    if not os.path.exists(token_path):
        raise Exception('No LinkedIn token at all — personal auth needed first')
    token = json.load(open(token_path))['access_token']
    # Test org admin access — if Community Management API is approved, this returns data
    req = urllib.request.Request(
        'https://api.linkedin.com/v2/organizationalEntityAcls?q=roleAssignee&role=ADMINISTRATOR',
        headers={'Authorization': f'Bearer {token}'}
    )
    try:
        data = json.loads(urllib.request.urlopen(req, timeout=10).read())
        elements = data.get('elements', [])
        if elements:
            return {
                'status_detail': '🎉 Community Management API APPROVED!',
                'org_count': len(elements),
                'billing': 'Free (Development Tier)',
                'action_needed': 'Re-auth with org scopes to enable posting'
            }
        return {
            'status_detail': 'API accessible but no org admin roles found',
            'billing': 'Free (Development Tier)',
        }
    except urllib.error.HTTPError as e:
        if e.code == 403:
            raise Exception('Community Management API still in review — 403 Forbidden on org endpoint')
        raise Exception(f'HTTP {e.code} checking org access')


def check_playwright():
    r = subprocess.run(['playwright', '--version'], capture_output=True, text=True, timeout=10)
    if r.returncode == 0:
        return {'version': r.stdout.strip(), 'billing': 'Free, unlimited', 'stealth': 'playwright-stealth v2.0.2'}
    raise Exception('playwright not available')


def check_imagemagick():
    r = subprocess.run(['convert', '--version'], capture_output=True, text=True, timeout=10)
    if r.returncode == 0:
        ver = r.stdout.split('\n')[0] if r.stdout else '?'
        return {'version': ver, 'billing': 'Free, unlimited'}
    raise Exception('ImageMagick not available')


def check_microlink():
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    req = urllib.request.Request('https://api.microlink.io/?url=https://example.com')
    try:
        resp = urllib.request.urlopen(req, timeout=10, context=ctx)
        data = json.loads(resp.read())
        return {'billing': 'Free tier: 50 req/day. Pro: $15.99/mo', 'note': 'No API key needed'}
    except urllib.error.HTTPError as e:
        if e.code == 429:
            return {'billing': 'Free tier: 50 req/day', 'note': 'Rate limited — daily quota used'}
        raise


def check_vercel():
    r = subprocess.run(['vercel', '--version'], capture_output=True, text=True, timeout=10)
    if r.returncode == 0:
        return {'version': r.stdout.strip().split('\n')[0], 'billing': 'Hobby plan (free). Auto-deploy on git push.'}
    raise Exception('vercel CLI not available')


def check_openclaw():
    r = subprocess.run(['openclaw', '--version'], capture_output=True, text=True, timeout=10)
    ver = r.stdout.strip() if r.stdout else r.stderr.strip()
    return {'version': ver, 'billing': 'Paid subscription'}

# ─── Background Services ─────────────────────────────────────────

def check_screen_sessions():
    r = subprocess.run(['screen', '-ls'], capture_output=True, text=True, timeout=5)
    out = r.stdout + r.stderr
    sessions = []
    for line in out.split('\n'):
        line = line.strip()
        if '.' in line and ('Detached' in line or 'Attached' in line):
            parts = line.split('\t')
            name = parts[0].split('.', 1)[1] if '.' in parts[0] else parts[0]
            status = 'Detached' if 'Detached' in line else 'Attached'
            sessions.append({'name': name, 'status': status})
    return sessions


def check_ports():
    ports = []
    for port, name in [(3055, 'figma-relay'), (80, 'webserver')]:
        try:
            req = urllib.request.Request(f'http://localhost:{port}/')
            urllib.request.urlopen(req, timeout=3)
            ports.append({'port': port, 'name': name, 'status': '✅'})
        except urllib.error.URLError:
            ports.append({'port': port, 'name': name, 'status': '❌'})
        except Exception:
            ports.append({'port': port, 'name': name, 'status': '⚠️'})
    return ports

# ─── Main ─────────────────────────────────────────────────────────

def main():
    output = {
        'timestamp': datetime.now(LISBON_TZ).isoformat(),
        'google_accounts': [],
        'services': [],
        'background': {},
    }

    # Google accounts
    accounts = [
        (os.path.join(GOOGLE_AUTH_DIR, 'token.json'), 'kitt@curiousendeavor.com'),
        (os.path.join(GOOGLE_AUTH_DIR, 'token-personal.json'), 'assafdagancos@gmail.com'),
    ]
    for token_path, label in accounts:
        output['google_accounts'].append(check_google_account(token_path, label))

    # All services
    all_checks = [
        ('Notion', check_notion),
        ('Figma', check_figma),
        ('GitHub CLI', check_github),
        ('Gemini AI', check_gemini),
        ('Recraft', check_recraft),
        ('X — @assafdagan', check_xurl_assaf),
        ('X — @Kitt_Curious', check_xurl_kitt),
        ('LinkedIn (Personal)', check_linkedin_personal),
        ('LinkedIn (Org Pages)', check_linkedin_org),
        ('Playwright', check_playwright),
        ('ImageMagick', check_imagemagick),
        ('Microlink API', check_microlink),
        ('Vercel CLI', check_vercel),
        ('OpenClaw', check_openclaw),
    ]

    for name, fn in all_checks:
        output['services'].append(check_service(name, fn))

    # Background services
    output['background']['screen_sessions'] = check_screen_sessions()
    output['background']['ports'] = check_ports()

    print(json.dumps(output, indent=2, default=str))


if __name__ == '__main__':
    main()
