#!/usr/bin/env python3
"""
GHL OAuth Private App Creator v3
Uses playwright-stealth to bypass reCAPTCHA bot detection.
Uses persistent browser context to maintain login state.
"""

import json
import time
import sys
import os
from pathlib import Path
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout
from playwright_stealth import Stealth

# Configuration
MARKETPLACE_URL = "https://marketplace.gohighlevel.com"
LOGIN_URL = "https://marketplace.gohighlevel.com/login"
EMAIL = "kinan@agileadapt.com"
PASSWORD = "SystemBird505*"
APP_NAME = "Genesis CRM Bridge"
REDIRECT_URI = "https://api.sunaivadigital.com/api/ghl/oauth/callback"
SCREENSHOT_DIR = Path("/mnt/e/genesis-system/GHL/oauth/screenshots")
CREDENTIALS_FILE = Path("/mnt/e/genesis-system/GHL/oauth/app_credentials.json")
BROWSER_PROFILE = "/mnt/e/genesis-system/.browser-data/ghl-marketplace-profile"

REQUIRED_SCOPES = [
    "contacts.readonly", "contacts.write",
    "conversations.readonly", "conversations.write",
    "conversations/message.readonly", "conversations/message.write",
    "calendars.readonly", "calendars.write",
    "calendars/events.readonly", "calendars/events.write",
    "opportunities.readonly", "opportunities.write",
    "locations.readonly", "locations.write",
    "locations/customFields.readonly", "locations/customFields.write",
    "locations/customValues.readonly", "locations/customValues.write",
    "locations/tags.readonly", "locations/tags.write",
    "workflows.readonly",
    "oauth.readonly",
    "oauth.write",
]

step_num = [0]

def ss(page, name):
    step_num[0] += 1
    path = SCREENSHOT_DIR / f"{step_num[0]:02d}_{name}.png"
    page.screenshot(path=str(path), full_page=False)
    print(f"  [SS] {path.name}")
    return path

def dump(page):
    print(f"  URL: {page.url}")

def is_logged_in(page):
    """Check if we're logged in by looking for user-specific elements."""
    # If the page shows "Sign In" in nav, we're NOT logged in
    sign_in = page.locator('a:has-text("Sign In"), button:has-text("Sign In")').first
    try:
        if sign_in.is_visible(timeout=2000):
            return False
    except:
        pass
    # If we see My Apps, profile avatar, or other logged-in indicators, we ARE logged in
    logged_in_indicators = [
        'text="My Apps"',
        'text="Logout"',
        'text="Sign Out"',
        '[class*="avatar"]',
        '[class*="profile"]',
    ]
    for sel in logged_in_indicators:
        try:
            if page.locator(sel).first.is_visible(timeout=1000):
                return True
        except:
            continue
    return False


def main():
    SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
    Path(BROWSER_PROFILE).mkdir(parents=True, exist_ok=True)

    print("=" * 70)
    print("GHL OAuth App Creator v3 — Stealth + Persistent Profile")
    print("=" * 70)

    with sync_playwright() as p:
        # Use persistent context (saves cookies/login state)
        context = p.chromium.launch_persistent_context(
            user_data_dir=BROWSER_PROFILE,
            headless=True,
            args=[
                "--no-sandbox",
                "--disable-blink-features=AutomationControlled",
                "--disable-features=IsolateOrigins,site-per-process",
                "--disable-dev-shm-usage",
                "--disable-gpu",
            ],
            viewport={"width": 1920, "height": 1080},
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.108 Safari/537.36",
            ignore_https_errors=True,
            locale="en-US",
            timezone_id="Australia/Brisbane",
        )

        page = context.pages[0] if context.pages else context.new_page()
        page.set_default_timeout(30000)

        # Apply stealth to bypass bot detection
        stealth = Stealth()
        stealth.apply_stealth_sync(page)
        print("  Stealth mode ACTIVE")

        try:
            # ===== STEP 1: NAVIGATE TO LOGIN =====
            print("\n[STEP 1] Navigating to login page...")
            page.goto(LOGIN_URL, wait_until="domcontentloaded", timeout=60000)
            page.wait_for_timeout(3000)
            ss(page, "login_page")
            dump(page)

            # Check if already logged in (from persistent profile)
            if "login" not in page.url.lower():
                print("  May already be logged in (redirected away from login)")
                if is_logged_in(page):
                    print("  CONFIRMED: Already logged in!")
                    # Skip to Step 5
                else:
                    print("  Not logged in, but not on login page. Navigating again...")
                    page.goto(LOGIN_URL, wait_until="domcontentloaded", timeout=30000)
                    page.wait_for_timeout(3000)

            # ===== STEP 2: FILL AND SUBMIT LOGIN =====
            print("\n[STEP 2] Filling login form...")

            # Wait for email input
            email_sel = 'input[placeholder="Enter your email"]'
            try:
                page.wait_for_selector(email_sel, timeout=10000)
            except:
                # Try broader selector
                email_sel = 'input[type="email"]'
                try:
                    page.wait_for_selector(email_sel, timeout=5000)
                except:
                    print("  No email input found - checking if already logged in...")
                    ss(page, "no_login_form")
                    if is_logged_in(page):
                        print("  Already logged in!")

            email_input = page.locator(email_sel).first
            if email_input.is_visible():
                email_input.fill("")  # Clear first
                page.wait_for_timeout(200)
                # Type slowly to look more human
                email_input.type(EMAIL, delay=50)
                print(f"  Typed email: {EMAIL}")

                # Tab to password
                page.keyboard.press("Tab")
                page.wait_for_timeout(300)

                # Fill password
                pwd_input = page.locator('input[type="password"]').first
                if pwd_input.is_visible():
                    pwd_input.type(PASSWORD, delay=30)
                    print("  Typed password")

                page.wait_for_timeout(1000)
                ss(page, "creds_filled")

                # Click Sign in
                print("\n[STEP 3] Clicking Sign in...")
                sign_in_btn = page.locator('button:has-text("Sign in")').first
                if sign_in_btn.is_visible():
                    sign_in_btn.click()
                    print("  Clicked 'Sign in'")
                else:
                    # Press Enter
                    page.keyboard.press("Enter")
                    print("  Pressed Enter")

                # Wait for login to process
                print("  Waiting for login response...")
                page.wait_for_timeout(10000)
                ss(page, "after_login")
                dump(page)

                # Check result
                if "login" in page.url.lower():
                    print("  WARNING: Still on login page - login may have failed")
                    # Check for error messages
                    error_text = page.locator('.error, [role="alert"], .toast-error, .Toastify__toast--error').all_text_contents()
                    if error_text:
                        print(f"  Error messages: {error_text}")

                    # Check for reCAPTCHA challenge
                    recaptcha = page.locator('iframe[src*="recaptcha"], iframe[title*="recaptcha"], .g-recaptcha, [class*="recaptcha"]')
                    try:
                        if recaptcha.first.is_visible(timeout=2000):
                            print("  reCAPTCHA DETECTED - this requires Collaborative Browser Handoff")
                    except:
                        pass

                    ss(page, "login_failed_state")

                    # Try waiting longer - sometimes login is slow
                    print("  Waiting additional 10s for delayed redirect...")
                    page.wait_for_timeout(10000)
                    ss(page, "login_delayed_check")
                    dump(page)
                else:
                    print("  Login appears successful!")

            # ===== STEP 4: CHECK IF LOGGED IN =====
            print("\n[STEP 4] Verifying login status...")
            page.goto(MARKETPLACE_URL, wait_until="domcontentloaded", timeout=30000)
            page.wait_for_timeout(5000)
            ss(page, "marketplace_after_login")
            dump(page)

            logged_in = is_logged_in(page)
            print(f"  Logged in: {logged_in}")

            if not logged_in:
                # Save the page HTML for analysis
                html = page.content()
                with open(SCREENSHOT_DIR / "marketplace_html.html", "w") as f:
                    f.write(html)

                # Get page text
                text = page.evaluate("document.body.innerText")
                print(f"  Page text (first 500 chars): {text[:500]}")

                # Look at what's in the top nav
                nav_text = page.locator('nav, header, [class*="nav"], [class*="header"]').first
                try:
                    if nav_text.is_visible():
                        print(f"  Nav content: {nav_text.text_content()[:200]}")
                except:
                    pass

                print("\n  *** LOGIN FAILED ***")
                print("  The GHL Marketplace has reCAPTCHA/bot detection.")
                print("  This requires Collaborative Browser Handoff:")
                print("  1. Human logs in via browser (bypasses reCAPTCHA)")
                print("  2. Cookies are saved to persistent profile")
                print("  3. Script resumes with authenticated session")
                print(f"\n  Browser profile: {BROWSER_PROFILE}")
                print("  Re-run this script after manual login.")

                # Save state
                with open(CREDENTIALS_FILE, "w") as f:
                    json.dump({
                        "status": "LOGIN_FAILED",
                        "reason": "reCAPTCHA/bot detection blocking headless login",
                        "action_needed": "Collaborative Browser Handoff - human login required",
                        "browser_profile": BROWSER_PROFILE,
                    }, f, indent=2)

                context.close()
                return

            # ===== STEP 5: NAVIGATE TO MY APPS =====
            print("\n[STEP 5] Navigating to My Apps...")

            # Try clicking "My Apps" in nav
            my_apps = page.locator('a:has-text("My Apps")').first
            try:
                if my_apps.is_visible(timeout=3000):
                    my_apps.click()
                    page.wait_for_timeout(5000)
                    ss(page, "my_apps_clicked")
                    dump(page)
            except:
                # Try direct URL
                page.goto("https://marketplace.gohighlevel.com/apps", wait_until="domcontentloaded", timeout=30000)
                page.wait_for_timeout(5000)
                ss(page, "my_apps_direct")
                dump(page)

            # ===== STEP 6: CREATE APP =====
            print("\n[STEP 6] Creating new app...")

            # Look for Create App button
            create_btn = page.locator('button:has-text("Create App")').first
            try:
                if create_btn.is_visible(timeout=5000):
                    create_btn.click()
                    page.wait_for_timeout(5000)
                    ss(page, "create_form")
            except:
                # Try other button text
                for txt in ["Create App", "New App", "Add App", "Create"]:
                    btn = page.locator(f'button:has-text("{txt}")').first
                    try:
                        if btn.is_visible(timeout=2000):
                            btn.click()
                            page.wait_for_timeout(5000)
                            break
                    except:
                        continue
                    # Try links too
                    link = page.locator(f'a:has-text("{txt}")').first
                    try:
                        if link.is_visible(timeout=2000):
                            link.click()
                            page.wait_for_timeout(5000)
                            break
                    except:
                        continue
                ss(page, "create_search")

            dump(page)

            # ===== STEP 7: FILL APP DETAILS =====
            print("\n[STEP 7] Filling app details...")

            # Find and fill the app name
            name_input = page.locator('input[placeholder*="name" i]').first
            try:
                if not name_input.is_visible(timeout=3000):
                    # Try first text input
                    name_input = page.locator('input[type="text"]').first
            except:
                name_input = page.locator('input[type="text"]').first

            if name_input.is_visible():
                name_input.fill(APP_NAME)
                print(f"  Filled name: {APP_NAME}")

            ss(page, "name_filled")

            # Select Private app type
            print("  Selecting Private app type...")
            for sel in ['label:has-text("Private")', 'button:has-text("Private")', 'div:has-text("Private")']:
                try:
                    elem = page.locator(sel).first
                    if elem.is_visible(timeout=2000):
                        elem.click()
                        print(f"  Selected Private")
                        break
                except:
                    continue

            page.wait_for_timeout(500)

            # Select Agency distribution
            print("  Selecting Agency distribution...")
            for sel in ['label:has-text("Agency")', 'button:has-text("Agency")', 'div:has-text("Agency")']:
                try:
                    elem = page.locator(sel).first
                    if elem.is_visible(timeout=2000):
                        elem.click()
                        print(f"  Selected Agency")
                        break
                except:
                    continue

            page.wait_for_timeout(500)
            ss(page, "details_filled")

            # ===== STEP 8: REDIRECT URI =====
            print("\n[STEP 8] Setting redirect URI...")
            redir_input = page.locator('input[placeholder*="redirect" i], input[placeholder*="URI" i], input[type="url"]').first
            try:
                if redir_input.is_visible(timeout=3000):
                    redir_input.fill(REDIRECT_URI)
                    print(f"  Filled: {REDIRECT_URI}")
            except:
                print("  Redirect URI input not found")

            ss(page, "redirect_uri")

            # ===== STEP 9: SELECT SCOPES =====
            print("\n[STEP 9] Selecting scopes...")

            # Navigate to scopes section if needed
            for tab_text in ["Scopes", "Permissions", "Access"]:
                try:
                    tab = page.locator(f'button:has-text("{tab_text}")').first
                    if tab.is_visible(timeout=2000):
                        tab.click()
                        page.wait_for_timeout(2000)
                        print(f"  Clicked '{tab_text}' tab")
                        break
                except:
                    continue

            selected = 0
            missing = []

            for scope in REQUIRED_SCOPES:
                found = False
                for sel in [
                    f'input[value="{scope}"]',
                    f'label:has-text("{scope}")',
                    f'span:has-text("{scope}")',
                ]:
                    try:
                        elem = page.locator(sel).first
                        if elem.is_visible(timeout=500):
                            tag = elem.evaluate("el => el.tagName")
                            if tag == "INPUT":
                                if not elem.is_checked():
                                    elem.check()
                            else:
                                elem.click()
                            found = True
                            selected += 1
                            break
                    except:
                        continue

                if not found:
                    missing.append(scope)
                page.wait_for_timeout(100)

            print(f"  Selected: {selected}/{len(REQUIRED_SCOPES)}")
            if missing:
                print(f"  Missing: {missing[:5]}{'...' if len(missing) > 5 else ''}")

            ss(page, "scopes_done")

            # ===== STEP 10: SAVE =====
            print("\n[STEP 10] Saving app...")
            for btn_text in ["Save", "Create", "Submit"]:
                try:
                    btn = page.locator(f'button:has-text("{btn_text}")').first
                    if btn.is_visible(timeout=2000):
                        btn.click()
                        print(f"  Clicked '{btn_text}'")
                        page.wait_for_timeout(8000)
                        break
                except:
                    continue

            ss(page, "saved")
            dump(page)

            # ===== STEP 11: CAPTURE CREDENTIALS =====
            print("\n[STEP 11] Capturing credentials...")
            page_text = page.evaluate("document.body.innerText")

            client_id = None
            client_secret = None

            # Search page text for patterns
            import re
            # Client ID pattern (UUID or long string)
            id_match = re.search(r'Client\s*ID[:\s]*([a-f0-9-]{20,})', page_text, re.I)
            if id_match:
                client_id = id_match.group(1)

            secret_match = re.search(r'Client\s*Secret[:\s]*([a-zA-Z0-9_-]{20,})', page_text, re.I)
            if secret_match:
                client_secret = secret_match.group(1)

            # Also try getting from input fields
            if not client_id:
                for sel in ['input[name*="clientId" i]', 'input[name*="client_id" i]']:
                    try:
                        inp = page.locator(sel).first
                        if inp.is_visible(timeout=1000):
                            client_id = inp.input_value()
                            break
                    except:
                        continue

            if not client_secret:
                for sel in ['input[name*="clientSecret" i]', 'input[name*="client_secret" i]']:
                    try:
                        inp = page.locator(sel).first
                        if inp.is_visible(timeout=1000):
                            client_secret = inp.input_value()
                            break
                    except:
                        continue

            ss(page, "final")

            # Save results
            creds = {
                "app_name": APP_NAME,
                "client_id": client_id,
                "client_secret": client_secret,
                "redirect_uri": REDIRECT_URI,
                "scopes_selected": selected,
                "scopes_missing": missing,
                "created_at": time.strftime("%Y-%m-%dT%H:%M:%S"),
                "page_url": page.url,
                "page_text_excerpt": page_text[:3000],
            }
            with open(CREDENTIALS_FILE, "w") as f:
                json.dump(creds, f, indent=2)

            print(f"\n{'='*70}")
            print("RESULT")
            print(f"{'='*70}")
            print(f"  Client ID:     {client_id or 'NOT CAPTURED'}")
            print(f"  Client Secret: {client_secret or 'NOT CAPTURED'}")
            print(f"  Scopes:        {selected}/{len(REQUIRED_SCOPES)}")

        except Exception as e:
            print(f"\nFATAL: {e}")
            import traceback
            traceback.print_exc()
            ss(page, "FATAL")
        finally:
            context.close()


if __name__ == "__main__":
    main()
