#!/usr/bin/env python3
"""
Macro: Reddit App Setup
Purpose: Create a Reddit API application for the Latent Signal ingestor
Justification: User's own account, no API alternative for app creation
"""

import asyncio
import os
import random
from datetime import datetime
from pathlib import Path

from patchright.async_api import async_playwright

# Paths
MACRO_DIR = Path(__file__).parent
SCREENSHOTS_DIR = MACRO_DIR / "screenshots"
ENV_FILE = MACRO_DIR.parent.parent / ".env"

SCREENSHOTS_DIR.mkdir(exist_ok=True)

# App configuration
APP_NAME = "LatentSignalBot"
APP_DESCRIPTION = "Latent signal detection for adverse event monitoring"
APP_REDIRECT_URI = os.getenv("REDDIT_REDIRECT_URI", "http://localhost:8080")


async def human_delay(min_ms=500, max_ms=1500):
    """Add human-like delay between actions."""
    await asyncio.sleep(random.randint(min_ms, max_ms) / 1000)


async def screenshot(page, name: str):
    """Capture a timestamped screenshot."""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    path = SCREENSHOTS_DIR / f"{timestamp}_{name}.png"
    await page.screenshot(path=str(path))
    print(f"[Screenshot] {path.name}")
    return path


async def human_type(page, selector: str, text: str):
    """Type text with human-like delays."""
    await page.click(selector)
    await human_delay(100, 300)
    for char in text:
        await page.keyboard.type(char)
        await asyncio.sleep(random.uniform(0.03, 0.10))
    await human_delay(200, 400)


async def wait_for_login(page):
    """Wait for user to complete login if needed."""
    print("\n[*] Checking if login is required...")
    await screenshot(page, "01_initial_load")

    # Check if we're on a login page or need to log in
    login_button = await page.query_selector('a[href*="login"], button:has-text("Log In")')

    if login_button:
        print("\n" + "=" * 50)
        print("LOGIN REQUIRED")
        print("=" * 50)
        print("Please log in to Reddit in the browser window.")
        print("")
        print("TIP: Use Reddit username/password login, NOT Google OAuth")
        print("     (Google detects automated browsers)")
        print("")
        print("The macro will continue automatically after login.")
        print("=" * 50 + "\n")

        # Wait for the apps page to load (indicates successful login)
        # Poll instead of wait_for_url to be more resilient
        for _ in range(600):  # 10 minutes max
            await asyncio.sleep(1)
            url = page.url
            if "/prefs/apps" in url:
                print("[OK] Login detected!")
                await human_delay(1000, 2000)
                return
            # Also check if we're now logged in on the current page
            login_btn = await page.query_selector('a[href*="login"], button:has-text("Log In")')
            if not login_btn:
                # Logged in, navigate to apps page
                print("[OK] Login detected, navigating to apps page...")
                await page.goto("https://www.reddit.com/prefs/apps")
                await page.wait_for_load_state("networkidle")
                await human_delay(1000, 2000)
                return

        print("[!] Login timeout - please try again")
        raise Exception("Login timeout")


async def create_app(page):
    """Create a new Reddit application."""
    print("\n[*] Looking for 'create app' button...")
    await screenshot(page, "02_apps_page")

    # Look for the "create another app" or "are you a developer? create an app" link
    create_button = await page.query_selector('button:has-text("create another app"), a:has-text("create another app"), button:has-text("create app"), a:has-text("create an app")')

    if not create_button:
        # Try alternative selectors for Reddit's UI
        create_button = await page.query_selector('.create-app-button, [href*="create"], button[type="submit"]')

    if not create_button:
        await screenshot(page, "02_no_create_button")
        print("[!] Could not find 'create app' button. Please check the screenshot.")
        print("[*] Attempting to scroll and find it...")

        # Scroll down to find it
        await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        await human_delay(500, 1000)
        await screenshot(page, "02b_scrolled")

        create_button = await page.query_selector('button:has-text("create"), a:has-text("create")')

    if create_button:
        print("[*] Found create button, clicking...")
        await human_delay(300, 600)
        await create_button.click()
        await human_delay(1000, 1500)
        await screenshot(page, "03_create_form")
    else:
        print("[!] Still can't find create button - manual intervention needed")
        input("Press Enter after clicking 'create another app' manually...")
        await screenshot(page, "03_manual_create_form")


async def fill_app_form(page):
    """Fill in the app creation form."""
    print("\n[*] Filling app form...")

    # Wait for form to be visible
    await human_delay(500, 1000)

    # Name field
    name_field = await page.query_selector('input[name="name"], #app-name, input[placeholder*="name"]')
    if name_field:
        await human_type(page, 'input[name="name"]', APP_NAME)
        print(f"    Name: {APP_NAME}")

    await human_delay(300, 600)

    # Select "script" type
    script_radio = await page.query_selector('input[value="script"], input[name="app_type"][value="script"]')
    if script_radio:
        await script_radio.click()
        print("    Type: script")
    else:
        # Try clicking label
        script_label = await page.query_selector('label:has-text("script")')
        if script_label:
            await script_label.click()
            print("    Type: script (via label)")

    await human_delay(300, 600)

    # Description field
    desc_field = await page.query_selector('textarea[name="description"], #app-description')
    if desc_field:
        await desc_field.click()
        await page.keyboard.type(APP_DESCRIPTION)
        print(f"    Description: {APP_DESCRIPTION}")

    await human_delay(300, 600)

    # Redirect URI field
    redirect_field = await page.query_selector('input[name="redirect_uri"], #app-redirect-uri')
    if redirect_field:
        await human_type(page, 'input[name="redirect_uri"]', APP_REDIRECT_URI)
        print(f"    Redirect URI: {APP_REDIRECT_URI}")

    await screenshot(page, "04_form_filled")

    # Submit the form
    print("\n[*] Submitting form...")
    await human_delay(500, 1000)

    submit_button = await page.query_selector('button[type="submit"]:has-text("create app"), input[type="submit"]')
    if submit_button:
        await submit_button.click()
    else:
        # Try generic submit
        await page.keyboard.press("Enter")

    await human_delay(2000, 3000)
    await screenshot(page, "05_after_submit")


async def extract_credentials(page):
    """Extract the client_id and client_secret from the page."""
    print("\n[*] Extracting credentials...")
    await screenshot(page, "06_credentials_page")

    # Get page content to find credentials
    content = await page.content()

    # The client_id appears under the app name (it's a short alphanumeric string)
    # The client_secret appears next to "secret"

    client_id = None
    client_secret = None

    # Try to find the app we just created
    app_info = await page.query_selector(f'div:has-text("{APP_NAME}")')

    if app_info:
        # Get all text in the app info section
        app_text = await app_info.inner_text()
        print(f"[DEBUG] App section text:\n{app_text[:500]}...")

        # Look for the client_id (appears right after "personal use script")
        # and secret
        lines = app_text.split('\n')
        for i, line in enumerate(lines):
            line = line.strip()
            if 'personal use script' in line.lower():
                # Next non-empty line should be client_id
                for next_line in lines[i+1:]:
                    next_line = next_line.strip()
                    if next_line and len(next_line) > 10 and len(next_line) < 30:
                        client_id = next_line
                        break
            if 'secret' in line.lower():
                # The secret might be on this line or next
                parts = line.split()
                for part in parts:
                    if len(part) > 20:
                        client_secret = part
                        break

    # Alternative: use JavaScript to extract
    if not client_id or not client_secret:
        print("[*] Trying JavaScript extraction...")

        # Reddit shows client_id in a specific element
        try:
            client_id = await page.evaluate("""
                () => {
                    const apps = document.querySelectorAll('.app-details, .developed-app');
                    for (const app of apps) {
                        const text = app.innerText;
                        if (text.includes('LatentSignalBot')) {
                            // Find the ID (short string after "personal use script")
                            const match = text.match(/personal use script[\\s\\n]+([a-zA-Z0-9_-]+)/i);
                            if (match) return match[1];
                        }
                    }
                    return null;
                }
            """)

            client_secret = await page.evaluate("""
                () => {
                    const apps = document.querySelectorAll('.app-details, .developed-app');
                    for (const app of apps) {
                        const text = app.innerText;
                        if (text.includes('LatentSignalBot')) {
                            // Find secret
                            const match = text.match(/secret[:\\s]+([a-zA-Z0-9_-]+)/i);
                            if (match) return match[1];
                        }
                    }
                    return null;
                }
            """)
        except Exception as e:
            print(f"[!] JavaScript extraction failed: {e}")

    return client_id, client_secret


async def save_credentials(client_id: str, client_secret: str):
    """Save credentials to .env file."""
    print(f"\n[*] Saving credentials to {ENV_FILE}")

    env_content = f"""REDDIT_CLIENT_ID={client_id}
REDDIT_CLIENT_SECRET={client_secret}
REDDIT_USER_AGENT=LatentSignalBot/0.1
"""

    # Read existing .env if it exists
    existing = {}
    if ENV_FILE.exists():
        with open(ENV_FILE) as f:
            for line in f:
                if '=' in line and not line.startswith('#'):
                    key, value = line.strip().split('=', 1)
                    existing[key] = value

    # Update with new values
    existing['REDDIT_CLIENT_ID'] = client_id
    existing['REDDIT_CLIENT_SECRET'] = client_secret
    existing['REDDIT_USER_AGENT'] = 'LatentSignalBot/0.1'

    # Write back
    with open(ENV_FILE, 'w') as f:
        for key, value in existing.items():
            f.write(f"{key}={value}\n")

    print(f"[OK] Credentials saved!")
    print(f"    REDDIT_CLIENT_ID={client_id}")
    print(f"    REDDIT_CLIENT_SECRET={'*' * len(client_secret)}")


async def main():
    print("=" * 50)
    print("Reddit App Setup Macro")
    print("=" * 50)

    # Use copied Chrome profile to avoid conflicts with running Chrome
    TEMP_PROFILE = Path("/tmp/chrome-jordanbot2000")

    async with async_playwright() as p:
        # Launch with copied profile
        context = await p.chromium.launch_persistent_context(
            user_data_dir=str(TEMP_PROFILE),
            channel="chrome",  # Use installed Chrome
            headless=False,
            viewport={"width": 1280, "height": 900},
            locale="en-US",
            timezone_id="America/New_York",
        )
        page = context.pages[0] if context.pages else await context.new_page()

        try:
            # Step 1: Navigate to Reddit apps page
            print("\n[*] Navigating to Reddit apps page...")
            await page.goto("https://www.reddit.com/prefs/apps")
            await page.wait_for_load_state("networkidle")
            await human_delay(1000, 2000)

            # Step 2: Handle login if needed
            await wait_for_login(page)

            # Step 3: Create new app
            await create_app(page)

            # Step 4: Fill form
            await fill_app_form(page)

            # Step 5: Extract credentials
            client_id, client_secret = await extract_credentials(page)

            if client_id and client_secret:
                # Step 6: Save to .env
                await save_credentials(client_id, client_secret)
                await screenshot(page, "99_success")
                print("\n" + "=" * 50)
                print("SUCCESS!")
                print("=" * 50)
                print(f"Credentials saved to: {ENV_FILE}")
                print("You can now run the Reddit ingestor:")
                print("  python main.py")
            else:
                await screenshot(page, "99_manual_needed")
                print("\n" + "=" * 50)
                print("MANUAL EXTRACTION NEEDED")
                print("=" * 50)
                print("Could not automatically extract credentials.")
                print("Please copy them manually from the browser:")
                print("  - Client ID: shown under 'personal use script'")
                print("  - Secret: shown next to 'secret'")
                print(f"\nThen add to: {ENV_FILE}")

                # Keep browser open for manual copying
                input("\nPress Enter when done copying credentials...")

        except Exception as e:
            await screenshot(page, "error")
            print(f"\n[ERROR] {e}")
            raise

        finally:
            print("\n[*] Closing browser...")
            await context.close()


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