#!/usr/bin/env python3
"""
Headed X Engagement v2 - Full growth mode
- Replies with LLM-generated content
- Auto-follow accounts that engage with us
- Follow FL cannabis community members
- Quote tweet dispensary deals
- Jump on trending hashtags

Usage:
  docker exec -e DISPLAY=:99 canna python3 /workspace/scripts/headed_engagement_v2.py
  
Options:
  --replies N     Number of replies (default: 2)
  --likes N       Number of likes (default: 4)  
  --follows N     Number of follows (default: 5)
  --check-notifs  Check notifications and follow back
  --quote-deals   Quote tweet dispensary deals instead of just posting
  --light         Light mode (1 reply, 2 likes, 2 follows)
  --heavy         Heavy mode (4 replies, 8 likes, 10 follows)
"""

import asyncio
import json
import os
import random
import sys
import urllib.request
from datetime import datetime
from pathlib import Path

if "DISPLAY" not in os.environ:
    os.environ["DISPLAY"] = ":99"

try:
    from patchright.async_api import async_playwright
except ImportError:
    print("pip install patchright")
    sys.exit(1)

PROFILE_DIR = Path("/workspace/browser_profile/x_headed")
COOKIES_PATH = Path("/workspace/scripts/x_cookies.json")
LOG_PATH = Path("/workspace/PROGRESS-LOG.md")
FOLLOWS_DB = Path("/workspace/follows_tracking.json")

VOICE_PROFILE = """You are a Florida medical cannabis patient in your late 20s. Helpful, friendly, genuine.
Lowercase twitter speak, 1-2 emojis max. Never sound like a brand or bot.

BE HELPFUL - If they're asking a question, ANSWER IT with real info:
- Recommend specific dispensaries (Jungle Boys, Flowery, Cookies for quality; Trulieve, Surterra for deals)
- Share actual strain experiences 
- Give real advice about the FL program (35-day rec limits, best times to shop, etc)

IMPORTANT - You know FL cannabis products:
- Flower = smokable bud (strains like Runtz, GMO, MAC, etc)
- Piatella/Rosin/Live Rosin/Hash = solventless concentrates you DAB (vaporize), NOT eat
- Shatter/Crumble/Wax = BHO concentrates you dab
- Vapes/Carts = vaporizer cartridges
- Edibles = food products (gummies, chocolates)
- RSO = Rick Simpson Oil, oral/sublingual

If they show a concentrate, talk about DABBING it. If they ask for recs, give real ones.
If they're showing a pickup, hype them up genuinely.
Effects: "melted", "couch locked", "blasted", "zooted", "smacked"

DON'T be generic. React to what they ACTUALLY said.

CRITICAL: If the tweet text is vague/short (like just "What you think?" with no context), 
ask a clarifying question or give a generic but safe response like "looks fire! what strain is that?"
NEVER assume what's in a video or image - you can't see them. Only react to the actual TEXT.

# Dispensary accounts to engage with
DISPENSARY_ACCOUNTS = [
    "Trulieve", "MUV_FL", "TheFloweryFL", "GrowHealthyFL",
    "SunburnCannabis", "curaleaffl", "JungleBoysFlorida",
    "CookiesFlorida", "saborflcannabis", "goldaborfl"
]

# Hashtags to search
FL_HASHTAGS = [
    "#FLMedicalTrees", "#FloridaCannabis", "#FloridaMMJ",
    "#FLDispensary", "#FloridaWeed", "#MMJFlorida"
]

# Accounts to potentially follow (FL cannabis community)
COMMUNITY_SEARCHES = [
    "florida medical cannabis", "FLMedicalTrees",
    "florida dispensary", "FL MMJ patient"
]


def log_action(action: str, details: str = ""):
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{ts}] {action}: {details}", flush=True)
    try:
        content = LOG_PATH.read_text() if LOG_PATH.exists() else "# PROGRESS-LOG.md\n"
        today = datetime.now().strftime("%Y-%m-%d")
        if f"### {today}" not in content:
            content += f"\n### {today}\n| Time | Action | Details |\n|------|--------|--------|\n"
        content += f"| {ts} | {action} | {details} |\n"
        LOG_PATH.write_text(content)
    except:
        pass


def load_cookies():
    if not COOKIES_PATH.exists():
        return []
    raw = json.loads(COOKIES_PATH.read_text())
    cookies = []
    for name in ["auth_token", "ct0", "twid", "kdt", "guest_id", "personalization_id"]:
        if name in raw and raw[name]:
            cookies.append({
                "name": name, "value": raw[name], "domain": ".x.com", "path": "/",
                "secure": True, "sameSite": "None" if name != "ct0" else "Lax"
            })
    return cookies


def load_follows_db():
    if FOLLOWS_DB.exists():
        return json.loads(FOLLOWS_DB.read_text())
    return {"followed": {}, "followers": [], "unfollowed": []}


def save_follows_db(db):
    FOLLOWS_DB.write_text(json.dumps(db, indent=2))


def generate_reply(tweet_text: str, username: str) -> str:
    if not tweet_text or len(tweet_text.strip()) < 10:
        return random.choice(["👀", "say less", "on my way 🏃"])
    
    prompt = f"""{VOICE_PROFILE}

Tweet from @{username}: "{tweet_text}"

Write ONE reply (under 150 chars). Be genuine and engaging. Just the reply:"""

    try:
        payload = json.dumps({
            "model": "claude-sonnet-4-20250514",
            "max_tokens": 80,
            "messages": [{"role": "user", "content": prompt}]
        }).encode()
        
        req = urllib.request.Request(
            "https://api.z.ai/api/anthropic/v1/messages",
            data=payload,
            headers={
                "Content-Type": "application/json",
                "x-api-key": "3887e2ed1a0a42ada0f9dc04461d3dce.O9dPzq1s2KQB2uMm",
                "anthropic-version": "2023-06-01"
            }
        )
        with urllib.request.urlopen(req, timeout=20) as resp:
            data = json.loads(resp.read().decode())
            reply = data["content"][0]["text"].strip().strip('"\'')
            if 5 < len(reply) <= 280:
                return reply
    except Exception as e:
        print(f"  LLM error: {e}", flush=True)
    
    return random.choice(["this is the way 🙌", "say less 🏃‍♂️", "need this energy today"])


def generate_quote_comment(deal_text: str, dispensary: str) -> str:
    prompt = f"""{VOICE_PROFILE}

Dispensary @{dispensary} posted this deal: "{deal_text}"

Write a SHORT quote tweet comment (under 100 chars) hyping this deal. Be excited but authentic. Just the comment:"""

    try:
        payload = json.dumps({
            "model": "claude-sonnet-4-20250514",
            "max_tokens": 60,
            "messages": [{"role": "user", "content": prompt}]
        }).encode()
        
        req = urllib.request.Request(
            "https://api.z.ai/api/anthropic/v1/messages",
            data=payload,
            headers={
                "Content-Type": "application/json",
                "x-api-key": "3887e2ed1a0a42ada0f9dc04461d3dce.O9dPzq1s2KQB2uMm",
                "anthropic-version": "2023-06-01"
            }
        )
        with urllib.request.urlopen(req, timeout=20) as resp:
            data = json.loads(resp.read().decode())
            comment = data["content"][0]["text"].strip().strip('"\'')
            if 5 < len(comment) <= 200:
                return comment
    except Exception as e:
        print(f"  LLM error: {e}", flush=True)
    
    return random.choice(["my wallet is crying but i'm going 😭", "don't tell me about this rn 🏃", "well there goes my plans"])


async def human_type(page, text: str):
    for char in text:
        await page.keyboard.type(char, delay=random.randint(35, 90))
        if random.random() < 0.02:
            await asyncio.sleep(random.uniform(0.2, 0.6))


async def dismiss_overlays(page):
    for sel in ['[data-testid="twc-cc-mask"]', '[data-testid="mask"]']:
        try:
            el = page.locator(sel)
            if await el.is_visible(timeout=300):
                await page.keyboard.press("Escape")
                await asyncio.sleep(0.3)
        except:
            pass
    
    for sel in ['[aria-label="Close"]', 'button:has-text("Got it")', 'button:has-text("Not now")']:
        try:
            el = page.locator(sel).first
            if await el.is_visible(timeout=300):
                await el.click()
                await asyncio.sleep(0.3)
        except:
            pass
    
    await page.keyboard.press("Escape")
    await asyncio.sleep(0.2)


async def ensure_xvfb():
    import subprocess
    display = os.environ.get("DISPLAY", ":99")
    display_num = display.replace(":", "")
    result = subprocess.run(["pgrep", "-f", f"Xvfb :{display_num}"], capture_output=True)
    if result.returncode != 0:
        print(f"🖥️  Starting Xvfb on {display}...", flush=True)
        subprocess.Popen(
            ["Xvfb", f":{display_num}", "-screen", "0", "1920x1080x24"],
            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
        )
        await asyncio.sleep(2)


async def follow_user(page, username: str, follows_db: dict) -> bool:
    """Follow a user. Returns True if successful."""
    try:
        # Check if already followed
        if username.lower() in [u.lower() for u in follows_db.get("followed", {}).keys()]:
            print(f"  Already following @{username}", flush=True)
            return False
        
        follow_btn = page.locator(f'[data-testid="follow"]').first
        if await follow_btn.is_visible(timeout=2000):
            await follow_btn.click()
            await asyncio.sleep(random.uniform(1, 2))
            
            # Record follow
            follows_db.setdefault("followed", {})[username] = {
                "followed_at": datetime.now().isoformat(),
                "followed_back": False
            }
            save_follows_db(follows_db)
            log_action("FOLLOW", f"@{username}")
            return True
    except Exception as e:
        print(f"  Follow error: {e}", flush=True)
    return False


async def check_notifications_and_follow_back(page, follows_db: dict, max_follows: int = 5) -> int:
    """Check notifications, follow back people who engaged with us."""
    followed_count = 0
    
    try:
        print("🔔 Checking notifications...", flush=True)
        await page.goto("https://x.com/notifications", wait_until="domcontentloaded")
        await asyncio.sleep(3)
        await dismiss_overlays(page)
        
        # Find notification items
        notifs = await page.locator('[data-testid="notification"]').all()
        print(f"  Found {len(notifs)} notifications", flush=True)
        
        for notif in notifs[:10]:
            if followed_count >= max_follows:
                break
            
            try:
                # Look for usernames in notification
                user_links = await notif.locator('a[href^="/"]').all()
                for link in user_links[:2]:
                    href = await link.get_attribute("href")
                    if href and href.startswith("/") and not href.startswith("/i/"):
                        username = href.strip("/").split("/")[0]
                        if username and username not in ["home", "notifications", "messages", "search"]:
                            # Check if we should follow back
                            if username.lower() not in [u.lower() for u in follows_db.get("followed", {}).keys()]:
                                # Go to their profile and follow
                                await page.goto(f"https://x.com/{username}", wait_until="domcontentloaded")
                                await asyncio.sleep(2)
                                await dismiss_overlays(page)
                                
                                if await follow_user(page, username, follows_db):
                                    followed_count += 1
                                    print(f"  ✅ Followed back @{username}", flush=True)
                                
                                await asyncio.sleep(random.uniform(2, 4))
                                break
            except:
                continue
        
        log_action("NOTIF_CHECK", f"Followed back {followed_count}")
        
    except Exception as e:
        print(f"  Notification check error: {e}", flush=True)
    
    return followed_count


async def follow_from_search(page, follows_db: dict, max_follows: int = 5) -> int:
    """Search for FL cannabis community and follow relevant accounts."""
    followed_count = 0
    
    search_term = random.choice(COMMUNITY_SEARCHES)
    print(f"🔍 Searching: {search_term}", flush=True)
    
    try:
        await page.goto(f"https://x.com/search?q={search_term.replace(' ', '%20')}&src=typed_query&f=user", wait_until="domcontentloaded")
        await asyncio.sleep(3)
        await dismiss_overlays(page)
        
        # Scroll a bit
        for _ in range(2):
            await page.mouse.wheel(0, random.randint(200, 400))
            await asyncio.sleep(1)
        
        # Find user cells
        users = await page.locator('[data-testid="UserCell"]').all()
        print(f"  Found {len(users)} users", flush=True)
        
        for user in users[:max_follows * 2]:
            if followed_count >= max_follows:
                break
            
            try:
                # Get username
                link = user.locator('a[href^="/"]').first
                href = await link.get_attribute("href")
                if href:
                    username = href.strip("/").split("/")[0]
                    
                    # Skip if already followed
                    if username.lower() in [u.lower() for u in follows_db.get("followed", {}).keys()]:
                        continue
                    
                    # Click follow button in cell
                    follow_btn = user.locator('[data-testid="follow"]')
                    if await follow_btn.is_visible(timeout=1000):
                        await follow_btn.click()
                        await asyncio.sleep(random.uniform(1.5, 3))
                        
                        follows_db.setdefault("followed", {})[username] = {
                            "followed_at": datetime.now().isoformat(),
                            "followed_back": False
                        }
                        save_follows_db(follows_db)
                        log_action("FOLLOW", f"@{username} (search)")
                        followed_count += 1
                        print(f"  ✅ Followed @{username}", flush=True)
            except:
                continue
        
    except Exception as e:
        print(f"  Search follow error: {e}", flush=True)
    
    return followed_count


async def engage_with_hashtag(page, stats: dict, follows_db: dict, max_actions: int = 3) -> None:
    """Find and engage with posts under FL cannabis hashtags."""
    hashtag = random.choice(FL_HASHTAGS)
    print(f"#️⃣ Engaging with {hashtag}...", flush=True)
    
    try:
        await page.goto(f"https://x.com/search?q={hashtag}&src=typed_query&f=live", wait_until="domcontentloaded")
        await asyncio.sleep(3)
        await dismiss_overlays(page)
        
        # Scroll
        for _ in range(2):
            await page.mouse.wheel(0, random.randint(200, 400))
            await asyncio.sleep(1)
        
        tweets = await page.locator('[data-testid="tweet"]').all()
        actions_taken = 0
        
        for tweet in tweets[:max_actions * 2]:
            if actions_taken >= max_actions:
                break
            
            try:
                await dismiss_overlays(page)
                
                # Like
                like_btn = tweet.locator('[data-testid="like"]')
                if await like_btn.is_visible():
                    await like_btn.click()
                    stats["likes"] += 1
                    actions_taken += 1
                    log_action("LIKE", f"{hashtag}")
                    await asyncio.sleep(random.uniform(1, 2))
                
            except:
                continue
        
        print(f"  {actions_taken} actions on {hashtag}", flush=True)
        
    except Exception as e:
        print(f"  Hashtag engage error: {e}", flush=True)


async def run_engagement(
    reply_count: int = 2,
    like_count: int = 4,
    follow_count: int = 5,
    check_notifs: bool = True,
    quote_deals: bool = False
):
    """Main engagement loop."""
    print("🥷 Headed X Engagement v2", flush=True)
    print(f"   Config: {reply_count}R / {like_count}L / {follow_count}F", flush=True)
    print(f"   DISPLAY={os.environ.get('DISPLAY', 'not set')}", flush=True)
    print("=" * 50, flush=True)
    
    await ensure_xvfb()
    
    PROFILE_DIR.mkdir(parents=True, exist_ok=True)
    stats = {"likes": 0, "replies": 0, "follows": 0, "quote_tweets": 0}
    follows_db = load_follows_db()
    
    async with async_playwright() as p:
        context = await p.chromium.launch_persistent_context(
            str(PROFILE_DIR),
            headless=False,
            viewport={"width": 1366, "height": 768},
            args=["--no-sandbox", "--disable-dev-shm-usage", "--disable-blink-features=AutomationControlled"]
        )
        
        cookies = load_cookies()
        if cookies:
            print(f"🍪 Injecting {len(cookies)} cookies...", flush=True)
            await context.add_cookies(cookies)
        
        page = context.pages[0] if context.pages else await context.new_page()
        
        try:
            # Load home
            print("📱 Loading X...", flush=True)
            await page.goto("https://x.com/home", wait_until="domcontentloaded")
            await asyncio.sleep(4)
            await dismiss_overlays(page)
            
            if "login" in page.url.lower():
                print("❌ Not logged in!", flush=True)
                await context.close()
                return stats
            
            log_action("SESSION", "Started v2 engagement")
            
            # Warmup scroll
            print("🔥 Warming up...", flush=True)
            for _ in range(3):
                await page.mouse.wheel(0, random.randint(200, 400))
                await asyncio.sleep(random.uniform(1, 2))
            
            # 1. Check notifications and follow back
            if check_notifs:
                notif_follows = await check_notifications_and_follow_back(page, follows_db, max_follows=3)
                stats["follows"] += notif_follows
            
            # 2. Follow from search
            remaining_follows = follow_count - stats["follows"]
            if remaining_follows > 0:
                search_follows = await follow_from_search(page, follows_db, max_follows=remaining_follows)
                stats["follows"] += search_follows
            
            # 3. Engage with hashtags
            await engage_with_hashtag(page, stats, follows_db, max_actions=like_count // 2)
            
            # 4. Reply to FRESH tweets (search recent #FLMedicalTrees content)
            search_queries = [
                "#FLMedicalTrees",
                "#FloridaCannabis", 
                "florida dispensary",
                "florida medical marijuana",
            ]
            
            for query in random.sample(search_queries, min(2, len(search_queries))):
                if stats["replies"] >= reply_count and stats["likes"] >= like_count:
                    break
                
                print(f"\n🔍 Searching fresh: {query}...", flush=True)
                
                try:
                    # Use f=live for LATEST tweets, not top
                    search_url = f"https://x.com/search?q={query.replace(' ', '%20')}&src=typed_query&f=live"
                    await page.goto(search_url, wait_until="domcontentloaded")
                    await asyncio.sleep(random.uniform(3, 5))
                    await dismiss_overlays(page)
                    
                    for _ in range(2):
                        await page.mouse.wheel(0, random.randint(150, 300))
                        await asyncio.sleep(random.uniform(0.8, 1.5))
                    
                    tweets = await page.locator('[data-testid="tweet"]').all()
                    print(f"   Found {len(tweets)} tweets", flush=True)
                    
                    for i, tweet in enumerate(tweets[:5]):
                        if stats["replies"] >= reply_count and stats["likes"] >= like_count:
                            break
                        
                        try:
                            await dismiss_overlays(page)
                            
                            # Get tweet time - skip if too old
                            time_el = tweet.locator('time')
                            tweet_time = ""
                            try:
                                tweet_time = await time_el.get_attribute('datetime') or ""
                            except:
                                pass
                            
                            # Get username from tweet
                            username = "unknown"
                            try:
                                user_link = tweet.locator('a[href^="/"][role="link"]').first
                                href = await user_link.get_attribute('href')
                                if href:
                                    username = href.strip('/').split('/')[0]
                            except:
                                pass
                            
                            # Like fresh tweets
                            if stats["likes"] < like_count:
                                like_btn = tweet.locator('[data-testid="like"]')
                                if await like_btn.is_visible():
                                    try:
                                        await like_btn.click(timeout=5000)
                                        stats["likes"] += 1
                                        log_action("LIKE", f"@{username} ({query})")
                                        print(f"   ❤️ Liked @{username}", flush=True)
                                        await asyncio.sleep(random.uniform(1.5, 3))
                                    except:
                                        pass
                            
                            # Reply to tweets that look like questions or discussions
                            if stats["replies"] < reply_count:
                                tweet_text = ""
                                try:
                                    txt = tweet.locator('[data-testid="tweetText"]')
                                    if await txt.count() > 0:
                                        tweet_text = await txt.inner_text()
                                except:
                                    pass
                                
                                # Skip if not a good engagement opportunity
                                tweet_lower = tweet_text.lower()
                                is_question = "?" in tweet_text
                                is_pickup = any(w in tweet_lower for w in ["picked up", "grabbed", "copped", "just got", "haul", "pickup"])
                                is_recommendation = any(w in tweet_lower for w in ["recommend", "suggestions", "best", "favorite", "thoughts on", "anyone tried", "which", "what strain", "where should"])
                                is_discussion = any(w in tweet_lower for w in ["anyone else", "does anyone", "has anyone", "am i the only", "unpopular opinion"])
                                
                                # Skip tweets that are just promos/ads
                                is_promo = any(w in tweet_lower for w in ["% off", "sale", "discount", "promo", "code:", "link in bio", "shop now"])
                                
                                # Skip tweets that are too vague (likely just video/image with no context)
                                is_too_vague = len(tweet_text.strip()) < 30 and not any(w in tweet_lower for w in ["strain", "dispo", "dispensary", "flower", "rosin", "hash", "edible", "cart", "vape"])
                                
                                if is_too_vague:
                                    print(f"   ⏭️ Skipping (too vague, likely video): {tweet_text[:40]}...", flush=True)
                                    continue
                                
                                if not (is_question or is_pickup or is_recommendation or is_discussion) or is_promo:
                                    print(f"   ⏭️ Skipping (not engaging): {tweet_text[:40]}...", flush=True)
                                    continue
                                
                                print(f"  💬 Attempting reply...", flush=True)
                                print(f"     Tweet: {tweet_text[:50]}...", flush=True)
                                
                                # Open tweet detail
                                try:
                                    tweet_link = tweet.locator('a[href*="/status/"]').first
                                    if await tweet_link.is_visible(timeout=2000):
                                        await tweet_link.click(timeout=5000)
                                        print(f"     Opened tweet detail...", flush=True)
                                        await asyncio.sleep(random.uniform(2, 3))
                                        await dismiss_overlays(page)
                                except Exception as e:
                                    print(f"     Detail open failed: {str(e)[:30]}", flush=True)
                                
                                # Generate reply
                                reply_text = generate_reply(tweet_text, username)
                                print(f"     Generated: {reply_text[:40]}...", flush=True)
                                
                                textarea = page.locator('[data-testid="tweetTextarea_0"]')
                                textarea_visible = False
                                
                                # Try keyboard shortcut 'r'
                                print(f"     Trying 'r' shortcut...", flush=True)
                                await page.keyboard.press("r")
                                await asyncio.sleep(random.uniform(2, 3))
                                await dismiss_overlays(page)
                                try:
                                    textarea_visible = await textarea.is_visible(timeout=3000)
                                except:
                                    pass
                                
                                # Fall back to clicking reply button
                                if not textarea_visible:
                                    print(f"     Trying reply button...", flush=True)
                                    reply_btn = page.locator('[data-testid="reply"]').first
                                    try:
                                        if await reply_btn.is_visible(timeout=2000):
                                            await reply_btn.click(timeout=5000)
                                            await asyncio.sleep(random.uniform(2, 3))
                                            await dismiss_overlays(page)
                                            textarea_visible = await textarea.is_visible(timeout=3000)
                                    except Exception as e:
                                        print(f"     Reply btn failed: {str(e)[:30]}", flush=True)
                                
                                print(f"     Textarea visible: {textarea_visible}", flush=True)
                                
                                if textarea_visible:
                                    await textarea.click(timeout=5000)
                                    await asyncio.sleep(0.3)
                                    await textarea.fill("")
                                    print(f"     Typing...", flush=True)
                                    await textarea.type(reply_text, delay=random.randint(40, 80))
                                    await asyncio.sleep(random.uniform(1, 2))
                                    
                                    # Submit with Ctrl+Enter
                                    print(f"     Submitting...", flush=True)
                                    await page.keyboard.press("Control+Enter")
                                    await asyncio.sleep(3)
                                    
                                    # Check success - textarea should be cleared
                                    try:
                                        current_text = await textarea.input_value()
                                        textarea_cleared = not current_text or current_text.strip() == ""
                                    except:
                                        textarea_cleared = True
                                    
                                    if textarea_cleared:
                                        stats["replies"] += 1
                                        log_action("REPLY", f"@{username}: {reply_text[:40]}...")
                                        print(f"  ✅ Replied: {reply_text[:40]}...", flush=True)
                                    else:
                                        print(f"  ⚠️ Reply may not have posted", flush=True)
                                else:
                                    print(f"  ⚠️ Could not open reply box", flush=True)
                                
                                await asyncio.sleep(random.uniform(2, 4))
                            
                        except Exception as e:
                            print(f"  Error: {str(e)[:50]}", flush=True)
                    
                    await asyncio.sleep(random.uniform(2, 4))
                    
                except Exception as e:
                    print(f"  Error searching {query}: {str(e)[:50]}", flush=True)
            
            print("\n" + "=" * 50, flush=True)
            print(f"📊 DONE - Likes: {stats['likes']}, Replies: {stats['replies']}, Follows: {stats['follows']}", flush=True)
            log_action("V2_DONE", f"L:{stats['likes']} R:{stats['replies']} F:{stats['follows']}")
            
        except Exception as e:
            print(f"Error: {e}", flush=True)
        finally:
            await context.close()
    
    return stats


async def main():
    if "--dry-run" in sys.argv:
        print("DRY RUN")
        return
    
    # Defaults
    reply_count = 2
    like_count = 4
    follow_count = 5
    check_notifs = "--check-notifs" in sys.argv or "--notifs" in sys.argv
    quote_deals = "--quote-deals" in sys.argv
    
    # Presets
    if "--heavy" in sys.argv:
        reply_count, like_count, follow_count = 4, 8, 10
        check_notifs = True
    elif "--light" in sys.argv:
        reply_count, like_count, follow_count = 1, 2, 2
    
    # Explicit overrides
    for i, arg in enumerate(sys.argv):
        if arg == "--replies" and i + 1 < len(sys.argv):
            reply_count = int(sys.argv[i + 1])
        elif arg == "--likes" and i + 1 < len(sys.argv):
            like_count = int(sys.argv[i + 1])
        elif arg == "--follows" and i + 1 < len(sys.argv):
            follow_count = int(sys.argv[i + 1])
    
    # Default to checking notifs
    if not any(x in sys.argv for x in ["--light", "--no-notifs"]):
        check_notifs = True
    
    print(f"Config: {reply_count}R / {like_count}L / {follow_count}F / notifs={check_notifs}", flush=True)
    await run_engagement(reply_count, like_count, follow_count, check_notifs, quote_deals)


if __name__ == "__main__":
    asyncio.run(main())
