#!/usr/bin/env python3
"""
Docker-based Stealth X Engagement using Patchright
Run inside the 'canna' container with persistent browser profile.

Usage:
  docker exec canna python3 /workspace/scripts/docker_stealth_engagement.py --login    # Open browser for manual login
  docker exec canna python3 /workspace/scripts/docker_stealth_engagement.py            # Run engagement
  docker exec canna python3 /workspace/scripts/docker_stealth_engagement.py --heavy    # More replies/likes
"""

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

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

# Persistent profile in mounted workspace
PROFILE_DIR = Path("/workspace/browser_profile/x_stealth")
LOG_PATH = Path("/workspace/PROGRESS-LOG.md")

# Voice profile for LLM replies
VOICE_PROFILE = """You are a Florida medical cannabis patient in your late 20s replying to dispensary tweets.

YOUR VIBE:
- Sassy, funny, a little sarcastic but not mean
- Self-deprecating humor about spending too much on weed
- Use lowercase, minimal punctuation, gen-z/millennial twitter speak
- Emojis sparingly (1-2 max, or none)
- Never sound like a brand or corporate account
- Never say "transparency" or generic marketing speak

REPLY STYLE:
- Short (under 150 chars ideally)
- React to what they ACTUALLY said
- Be funny/relatable - imagine friends seeing this"""

TARGET_ACCOUNTS = [
    "Trulieve", "MUV_FL", "TheFloweryFL", "GrowHealthyFL",
    "SunburnCannabis", "curaleaffl", "JungleBoysFlorida"
]


def log_action(action: str, details: str = "") -> None:
    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 generate_reply(tweet_text: str, username: str) -> str:
    """Generate reply via LLM (Z.ai API)."""
    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 tweet (under 150 chars). Just the reply, no quotes."""

    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 len(reply) <= 280 and len(reply) > 5:
                return reply
    except Exception as e:
        print(f"  LLM error: {e}", flush=True)
    
    # Fallback
    return random.choice([
        "my wallet didn't need this today",
        "say less 🏃‍♂️",
        "noted 📝"
    ])


async def human_type(page, text: str):
    """Type with human-like delays."""
    for char in text:
        await page.keyboard.type(char, delay=random.randint(40, 100))
        if random.random() < 0.03:
            await asyncio.sleep(random.uniform(0.3, 0.8))
    await asyncio.sleep(random.uniform(0.2, 0.4))


async def dismiss_overlays(page):
    """Dismiss X's annoying popups."""
    for selector in ['[data-testid="mask"]', '[aria-label="Close"]', 'button:has-text("Got it")']:
        try:
            elem = page.locator(selector).first
            if await elem.is_visible(timeout=500):
                await elem.click()
                await asyncio.sleep(0.3)
        except:
            pass
    await page.keyboard.press("Escape")
    await asyncio.sleep(0.2)


async def run_login():
    """Open browser for manual login - run with display."""
    print("🔐 Opening browser for manual X login...", flush=True)
    print("   Log in, then press Enter here when done.", flush=True)
    
    PROFILE_DIR.mkdir(parents=True, exist_ok=True)
    
    async with async_playwright() as p:
        context = await p.chromium.launch_persistent_context(
            str(PROFILE_DIR),
            headless=False,
            viewport={"width": 1366, "height": 768},
        )
        page = context.pages[0] if context.pages else await context.new_page()
        await page.goto("https://x.com/login")
        
        input("\nPress Enter after logging in...")
        
        # Verify login
        await page.goto("https://x.com/home")
        await asyncio.sleep(2)
        if "login" not in page.url.lower():
            print("✅ Login saved to persistent profile!")
        else:
            print("⚠️ Login may not have saved - check profile dir")
        
        await context.close()


def load_cookies():
    """Load cookies from JSON file."""
    cookies_path = Path("/workspace/scripts/x_cookies.json")
    if not cookies_path.exists():
        return []
    
    raw = json.loads(cookies_path.read_text())
    cookies = []
    cookie_map = {
        "auth_token": {"httpOnly": True, "secure": True, "sameSite": "None"},
        "ct0": {"httpOnly": False, "secure": True, "sameSite": "Lax"},
        "twid": {"httpOnly": False, "secure": True, "sameSite": "None"},
        "kdt": {"httpOnly": True, "secure": True, "sameSite": "Lax"},
        "guest_id": {"httpOnly": False, "secure": True, "sameSite": "None"},
        "personalization_id": {"httpOnly": False, "secure": True, "sameSite": "None"},
    }
    for name, value in raw.items():
        if name in cookie_map and value:
            cookies.append({
                "name": name, "value": value, "domain": ".x.com", "path": "/",
                **cookie_map[name]
            })
    return cookies


async def run_engagement(reply_count: int = 2, like_count: int = 5):
    """Run stealth engagement session."""
    print("🥷 Docker Stealth X Engagement", flush=True)
    print("=" * 50, flush=True)
    
    PROFILE_DIR.mkdir(parents=True, exist_ok=True)
    stats = {"likes": 0, "replies": 0}
    
    async with async_playwright() as p:
        context = await p.chromium.launch_persistent_context(
            str(PROFILE_DIR),
            headless=True,
            viewport={"width": 1366, "height": 768},
            args=["--no-sandbox", "--disable-dev-shm-usage"]
        )
        
        # Inject cookies at runtime
        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:
            # Check login
            print("📱 Loading X...", flush=True)
            await page.goto("https://x.com/home", wait_until="domcontentloaded")
            await asyncio.sleep(3)
            
            if "login" in page.url.lower():
                print("❌ Not logged in. Check x_cookies.json", flush=True)
                await context.close()
                return stats
            
            log_action("SESSION", "Logged in via persistent profile")
            
            # Warmup
            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))
            
            # Engage targets
            targets = random.sample(TARGET_ACCOUNTS, min(3, len(TARGET_ACCOUNTS)))
            
            for username in targets:
                if stats["likes"] >= like_count and stats["replies"] >= reply_count:
                    break
                    
                print(f"\n👤 @{username}...", flush=True)
                
                try:
                    await page.goto(f"https://x.com/{username}", wait_until="domcontentloaded")
                    await asyncio.sleep(random.uniform(2, 4))
                    await dismiss_overlays(page)
                    
                    # Scroll a bit
                    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()
                    
                    for i, tweet in enumerate(tweets[:3]):
                        if stats["likes"] >= like_count and stats["replies"] >= reply_count:
                            break
                        
                        try:
                            await dismiss_overlays(page)
                            
                            # Like
                            if stats["likes"] < like_count:
                                like_btn = tweet.locator('[data-testid="like"]')
                                if await like_btn.is_visible():
                                    await like_btn.click(timeout=3000)
                                    stats["likes"] += 1
                                    log_action("LIKE", f"@{username}")
                                    await asyncio.sleep(random.uniform(1, 2))
                            
                            # Reply (first tweet only)
                            if stats["replies"] < reply_count and i == 0:
                                # Get tweet text
                                tweet_text = ""
                                try:
                                    text_elem = tweet.locator('[data-testid="tweetText"]')
                                    if await text_elem.count() > 0:
                                        tweet_text = await text_elem.inner_text()
                                except:
                                    pass
                                
                                reply_btn = tweet.locator('[data-testid="reply"]')
                                if await reply_btn.is_visible():
                                    await reply_btn.click()
                                    await asyncio.sleep(random.uniform(1.5, 2.5))
                                    
                                    reply_text = generate_reply(tweet_text, username)
                                    
                                    textarea = page.locator('[data-testid="tweetTextarea_0"]')
                                    if await textarea.is_visible():
                                        await textarea.click()
                                        await human_type(page, reply_text)
                                        await asyncio.sleep(random.uniform(0.5, 1))
                                        
                                        submit = page.locator('[data-testid="tweetButton"]')
                                        if await submit.is_enabled():
                                            await submit.click()
                                            await asyncio.sleep(2)
                                            
                                            # Verify
                                            if not await textarea.is_visible():
                                                stats["replies"] += 1
                                                log_action("REPLY", f"@{username}: {reply_text[:40]}...")
                                                print(f"  ✅ {reply_text[:50]}...", flush=True)
                                            else:
                                                print(f"  ⚠️ Reply may have failed", flush=True)
                                                await page.keyboard.press("Escape")
                                        else:
                                            await page.keyboard.press("Escape")
                                    else:
                                        await page.keyboard.press("Escape")
                        except Exception as e:
                            print(f"  Error: {e}", flush=True)
                            continue
                    
                    await asyncio.sleep(random.uniform(2, 4))
                    
                except Exception as e:
                    print(f"  Error with @{username}: {e}", flush=True)
            
            # Summary
            print("\n" + "=" * 50, flush=True)
            print(f"📊 DONE - Likes: {stats['likes']}, Replies: {stats['replies']}", flush=True)
            log_action("STEALTH_DONE", f"L:{stats['likes']} R:{stats['replies']}")
            
        except Exception as e:
            print(f"Error: {e}", flush=True)
            ss = PROFILE_DIR.parent / f"error_{datetime.now().strftime('%H%M%S')}.png"
            await page.screenshot(path=str(ss))
        finally:
            await context.close()
    
    return stats


async def main():
    if "--login" in sys.argv:
        await run_login()
    elif "--dry-run" in sys.argv:
        print("DRY RUN - would engage with:", TARGET_ACCOUNTS[:3])
    else:
        reply_count = 4 if "--heavy" in sys.argv else 2
        like_count = 10 if "--heavy" in sys.argv else 5
        if "--light" in sys.argv:
            reply_count, like_count = 1, 3
        await run_engagement(reply_count, like_count)


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