#!/usr/bin/env python3
"""
GHL OAuth Private App Creator
Creates "Genesis CRM Bridge" app on marketplace.gohighlevel.com
Takes screenshots at each step for verification.
"""

import json
import time
import sys
import os
from pathlib import Path
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout

# Configuration
MARKETPLACE_URL = "https://marketplace.gohighlevel.com"
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")

# All 23 scopes we need
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",
]

def screenshot(page, name):
    """Take a screenshot with the given name."""
    path = SCREENSHOT_DIR / f"{name}.png"
    page.screenshot(path=str(path), full_page=False)
    print(f"[SCREENSHOT] {path}")
    return path


def wait_and_screenshot(page, name, wait_ms=2000):
    """Wait a bit then take a screenshot."""
    page.wait_for_timeout(wait_ms)
    return screenshot(page, name)


def step_login(page):
    """Step 1: Navigate to marketplace and log in."""
    print("\n=== STEP 1: Navigate to GHL Marketplace ===")
    page.goto(MARKETPLACE_URL, wait_until="networkidle", timeout=60000)
    wait_and_screenshot(page, "01_marketplace_landing")

    # Check if we're already logged in
    current_url = page.url
    print(f"Current URL: {current_url}")

    # Check if there's a login form or if we need to find login button
    # GHL marketplace might redirect to login or show a login button

    # Look for email input
    email_input = page.query_selector('input[type="email"], input[name="email"], input[placeholder*="email" i], input[placeholder*="Email" i]')
    if not email_input:
        # Maybe we need to click a login/sign-in button first
        login_btn = page.query_selector('text="Sign In" >> visible=true') or \
                    page.query_selector('text="Login" >> visible=true') or \
                    page.query_selector('text="Log In" >> visible=true') or \
                    page.query_selector('a[href*="login"]') or \
                    page.query_selector('button:has-text("Sign")')
        if login_btn:
            print("Found login button, clicking...")
            login_btn.click()
            page.wait_for_timeout(3000)
            wait_and_screenshot(page, "01b_after_login_click")
            email_input = page.query_selector('input[type="email"], input[name="email"], input[placeholder*="email" i]')

    if not email_input:
        # Maybe the page redirected to a login URL
        print(f"Current URL after navigation: {page.url}")
        # Try waiting for the page to fully load
        page.wait_for_timeout(5000)
        wait_and_screenshot(page, "01c_waiting_for_login_form")
        email_input = page.query_selector('input[type="email"], input[name="email"], input[placeholder*="email" i], input[placeholder*="Email" i]')

    if email_input:
        print("Found email input, filling credentials...")
        email_input.fill(EMAIL)
        page.wait_for_timeout(500)

        # Find password input
        password_input = page.query_selector('input[type="password"], input[name="password"]')
        if password_input:
            password_input.fill(PASSWORD)
            page.wait_for_timeout(500)
            wait_and_screenshot(page, "02_credentials_filled")

            # Click login/submit button
            submit_btn = page.query_selector('button[type="submit"]') or \
                        page.query_selector('button:has-text("Sign")') or \
                        page.query_selector('button:has-text("Log")') or \
                        page.query_selector('input[type="submit"]')
            if submit_btn:
                submit_btn.click()
                print("Clicked submit button...")
                page.wait_for_timeout(5000)
                wait_and_screenshot(page, "03_after_login")
                print(f"URL after login: {page.url}")
            else:
                # Try pressing Enter
                password_input.press("Enter")
                page.wait_for_timeout(5000)
                wait_and_screenshot(page, "03_after_enter")
                print(f"URL after login: {page.url}")
        else:
            print("WARNING: Could not find password input")
            wait_and_screenshot(page, "02_no_password_input")
    else:
        print("WARNING: Could not find email input - may already be logged in")
        wait_and_screenshot(page, "01d_no_email_input")
        # Check if we're on a dashboard/my-apps page already
        if "apps" in page.url.lower() or "dashboard" in page.url.lower():
            print("Appears to already be logged in!")

    return True


def step_navigate_my_apps(page):
    """Step 2: Navigate to My Apps."""
    print("\n=== STEP 2: Navigate to My Apps ===")

    # Try direct URL first
    page.goto("https://marketplace.gohighlevel.com/apps", wait_until="networkidle", timeout=30000)
    page.wait_for_timeout(3000)
    wait_and_screenshot(page, "04_my_apps_direct")
    print(f"URL: {page.url}")

    # If that didn't work, look for My Apps link/button
    if "apps" not in page.url.lower():
        my_apps_link = page.query_selector('text="My Apps"') or \
                       page.query_selector('a[href*="apps"]') or \
                       page.query_selector('text="Apps"')
        if my_apps_link:
            my_apps_link.click()
            page.wait_for_timeout(3000)
            wait_and_screenshot(page, "04b_my_apps_clicked")

    return True


def step_create_app(page):
    """Step 3: Click Create App button."""
    print("\n=== STEP 3: Click Create App ===")

    # Look for Create App button
    create_btn = page.query_selector('text="Create App"') or \
                 page.query_selector('button:has-text("Create")') or \
                 page.query_selector('a:has-text("Create App")')

    if create_btn:
        create_btn.click()
        print("Clicked Create App button")
        page.wait_for_timeout(3000)
        wait_and_screenshot(page, "05_create_app_form")
    else:
        # Maybe there's a + button or different UI
        print("Couldn't find 'Create App' button, taking screenshot of current page")
        wait_and_screenshot(page, "05_no_create_button")
        # Try looking for alternative create buttons
        plus_btn = page.query_selector('[data-testid*="create"]') or \
                   page.query_selector('.create-btn') or \
                   page.query_selector('button:has-text("New")')
        if plus_btn:
            plus_btn.click()
            page.wait_for_timeout(3000)
            wait_and_screenshot(page, "05b_alt_create_clicked")

    return True


def step_fill_app_details(page):
    """Step 4: Fill in app name, type, distribution type."""
    print("\n=== STEP 4: Fill App Details ===")

    # Fill app name
    name_input = page.query_selector('input[name="name"], input[placeholder*="name" i], input[placeholder*="Name" i], #name, #app-name, #appName')
    if not name_input:
        # Try label-based approach
        name_input = page.query_selector('label:has-text("App Name") + input') or \
                     page.query_selector('label:has-text("Name") + input') or \
                     page.query_selector('input[aria-label*="name" i]')

    if not name_input:
        # Broader search - first text input on the form
        all_inputs = page.query_selector_all('input[type="text"], input:not([type])')
        if all_inputs:
            name_input = all_inputs[0]
            print(f"Using first text input as name field (found {len(all_inputs)} text inputs)")

    if name_input:
        name_input.fill(APP_NAME)
        print(f"Filled app name: {APP_NAME}")
    else:
        print("WARNING: Could not find app name input")

    page.wait_for_timeout(1000)
    wait_and_screenshot(page, "06_name_filled")

    # Handle App Type - look for radio buttons or select for "Private"
    # Try clicking Private radio/option
    private_option = page.query_selector('text="Private"') or \
                     page.query_selector('input[value="private" i]') or \
                     page.query_selector('label:has-text("Private")') or \
                     page.query_selector('[data-value="private" i]')
    if private_option:
        private_option.click()
        print("Selected App Type: Private")
    else:
        print("Looking for App Type dropdown/selector...")
        # Try select dropdown
        type_select = page.query_selector('select[name*="type" i]')
        if type_select:
            type_select.select_option(label="Private")
            print("Selected Private from dropdown")

    page.wait_for_timeout(1000)

    # Handle Distribution Type - Agency
    agency_option = page.query_selector('text="Agency"') or \
                    page.query_selector('input[value="agency" i]') or \
                    page.query_selector('label:has-text("Agency")') or \
                    page.query_selector('[data-value="agency" i]')
    if agency_option:
        agency_option.click()
        print("Selected Distribution Type: Agency")
    else:
        print("Looking for Distribution Type dropdown/selector...")
        dist_select = page.query_selector('select[name*="distribution" i]')
        if dist_select:
            dist_select.select_option(label="Agency")
            print("Selected Agency from dropdown")

    page.wait_for_timeout(1000)
    wait_and_screenshot(page, "07_app_details_filled")

    return True


def step_set_redirect_uri(page):
    """Step 5: Set the redirect URI."""
    print("\n=== STEP 5: Set Redirect URI ===")

    # Look for redirect URI input
    redirect_input = page.query_selector('input[name*="redirect" i], input[placeholder*="redirect" i], input[placeholder*="URI" i], input[placeholder*="callback" i]')
    if not redirect_input:
        redirect_input = page.query_selector('label:has-text("Redirect") + input') or \
                         page.query_selector('label:has-text("redirect") ~ input') or \
                         page.query_selector('input[aria-label*="redirect" i]')

    if redirect_input:
        redirect_input.fill(REDIRECT_URI)
        print(f"Filled redirect URI: {REDIRECT_URI}")
    else:
        print("WARNING: Could not find redirect URI input - will look for it later")
        # It might be on a different tab/section

    page.wait_for_timeout(1000)
    wait_and_screenshot(page, "08_redirect_uri_filled")

    return True


def step_select_scopes(page):
    """Step 6: Select all 23 required scopes."""
    print("\n=== STEP 6: Select Scopes ===")

    # First, look for a scopes section/tab
    scopes_tab = page.query_selector('text="Scopes"') or \
                 page.query_selector('a:has-text("Scopes")') or \
                 page.query_selector('button:has-text("Scopes")') or \
                 page.query_selector('[data-tab="scopes" i]')
    if scopes_tab:
        scopes_tab.click()
        print("Clicked Scopes tab")
        page.wait_for_timeout(2000)
        wait_and_screenshot(page, "09_scopes_tab")

    selected_count = 0
    missing_scopes = []

    for scope in REQUIRED_SCOPES:
        # Try multiple strategies to find and select each scope
        found = False

        # Strategy 1: Look for checkbox/label with exact scope text
        scope_elem = page.query_selector(f'text="{scope}"')
        if scope_elem:
            # Check if it's a checkbox or near one
            checkbox = scope_elem.query_selector('input[type="checkbox"]') or \
                       page.query_selector(f'input[value="{scope}"]')
            if checkbox:
                if not checkbox.is_checked():
                    checkbox.click()
                found = True
            else:
                # Click the element itself (might be a toggle)
                scope_elem.click()
                found = True

        # Strategy 2: Look for checkbox with value matching scope
        if not found:
            checkbox = page.query_selector(f'input[value="{scope}"]') or \
                       page.query_selector(f'input[data-scope="{scope}"]')
            if checkbox:
                if not checkbox.is_checked():
                    checkbox.click()
                found = True

        # Strategy 3: Look for label containing scope text
        if not found:
            # Replace / with escaped version for selector
            scope_escaped = scope.replace("/", "\\/")
            label = page.query_selector(f'label:has-text("{scope}")') or \
                    page.query_selector(f'span:has-text("{scope}")')
            if label:
                label.click()
                found = True

        if found:
            selected_count += 1
            print(f"  [+] Selected: {scope}")
        else:
            missing_scopes.append(scope)
            print(f"  [-] NOT FOUND: {scope}")

        page.wait_for_timeout(300)  # Small delay between selections

    print(f"\nSelected {selected_count}/{len(REQUIRED_SCOPES)} scopes")
    if missing_scopes:
        print(f"Missing scopes: {missing_scopes}")

    wait_and_screenshot(page, "10_scopes_selected")

    # Scroll down to see more scopes if needed
    page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
    page.wait_for_timeout(1000)
    wait_and_screenshot(page, "10b_scopes_scrolled")

    return selected_count, missing_scopes


def step_save_app(page):
    """Step 7: Save/Create the app."""
    print("\n=== STEP 7: Save App ===")

    save_btn = page.query_selector('button:has-text("Save")') or \
               page.query_selector('button:has-text("Create")') or \
               page.query_selector('button[type="submit"]') or \
               page.query_selector('text="Save"') or \
               page.query_selector('text="Create"')

    if save_btn:
        save_btn.click()
        print("Clicked Save/Create button")
        page.wait_for_timeout(5000)
        wait_and_screenshot(page, "11_app_saved")
    else:
        print("WARNING: Could not find Save/Create button")
        wait_and_screenshot(page, "11_no_save_button")

    return True


def step_capture_credentials(page):
    """Step 8: Capture Client ID and Client Secret."""
    print("\n=== STEP 8: Capture Credentials ===")

    wait_and_screenshot(page, "12_credentials_page")

    client_id = None
    client_secret = None

    # Look for Client ID
    id_elem = page.query_selector('text="Client ID"') or \
              page.query_selector('label:has-text("Client ID")')
    if id_elem:
        # The value might be in a nearby input, span, or code element
        parent = id_elem.evaluate_handle("el => el.parentElement")
        id_input = page.evaluate("el => el.parentElement.querySelector('input, code, span.value, .copy-text, [class*=\"value\"]')?.textContent || el.parentElement.querySelector('input')?.value", id_elem)
        if id_input:
            client_id = id_input.strip()

    if not client_id:
        # Try finding by input name/id
        id_input = page.query_selector('input[name*="client_id" i], input[name*="clientId" i], #client_id, #clientId')
        if id_input:
            client_id = id_input.input_value()

    # Look for Client Secret
    secret_elem = page.query_selector('text="Client Secret"') or \
                  page.query_selector('label:has-text("Client Secret")')
    if secret_elem:
        parent = secret_elem.evaluate_handle("el => el.parentElement")
        secret_input = page.evaluate("el => el.parentElement.querySelector('input, code, span.value, .copy-text, [class*=\"value\"]')?.textContent || el.parentElement.querySelector('input')?.value", secret_elem)
        if secret_input:
            client_secret = secret_input.strip()

    if not client_secret:
        secret_input = page.query_selector('input[name*="client_secret" i], input[name*="clientSecret" i], #client_secret, #clientSecret')
        if secret_input:
            client_secret = secret_input.input_value()

    # Try to get all visible text on the page for manual extraction if needed
    page_text = page.evaluate("document.body.innerText")

    print(f"\nClient ID: {client_id or 'NOT FOUND'}")
    print(f"Client Secret: {client_secret or 'NOT FOUND'}")

    # Save to file
    creds = {
        "app_name": APP_NAME,
        "client_id": client_id,
        "client_secret": client_secret,
        "redirect_uri": REDIRECT_URI,
        "created_at": time.strftime("%Y-%m-%d %H:%M:%S"),
        "page_text_excerpt": page_text[:2000] if page_text else None
    }

    with open(CREDENTIALS_FILE, "w") as f:
        json.dump(creds, f, indent=2)
    print(f"\nCredentials saved to: {CREDENTIALS_FILE}")

    # Take a final screenshot
    wait_and_screenshot(page, "13_final_state")

    return client_id, client_secret


def main():
    SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)

    print("=" * 60)
    print("GHL OAuth Private App Creator")
    print("=" * 60)
    print(f"App Name: {APP_NAME}")
    print(f"Redirect URI: {REDIRECT_URI}")
    print(f"Scopes: {len(REQUIRED_SCOPES)}")
    print(f"Screenshots: {SCREENSHOT_DIR}")
    print()

    with sync_playwright() as p:
        # Launch browser (headed mode for visibility, persistent context for login state)
        browser = p.chromium.launch(
            headless=True,  # headless since we're in WSL
            args=[
                "--no-sandbox",
                "--disable-blink-features=AutomationControlled",
                "--disable-features=IsolateOrigins,site-per-process",
            ]
        )

        context = browser.new_context(
            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.0.0 Safari/537.36",
            ignore_https_errors=True,
        )

        page = context.new_page()
        page.set_default_timeout(30000)

        try:
            # Step 1: Login
            step_login(page)

            # Step 2: Navigate to My Apps
            step_navigate_my_apps(page)

            # Step 3: Click Create App
            step_create_app(page)

            # Step 4: Fill app details
            step_fill_app_details(page)

            # Step 5: Set redirect URI
            step_set_redirect_uri(page)

            # Step 6: Select scopes
            selected_count, missing_scopes = step_select_scopes(page)

            # Step 7: Save
            step_save_app(page)

            # Step 8: Capture credentials
            client_id, client_secret = step_capture_credentials(page)

            print("\n" + "=" * 60)
            print("RESULT SUMMARY")
            print("=" * 60)
            print(f"Client ID: {client_id or 'NOT CAPTURED'}")
            print(f"Client Secret: {client_secret or 'NOT CAPTURED'}")
            print(f"Scopes Selected: {selected_count}/{len(REQUIRED_SCOPES)}")
            if missing_scopes:
                print(f"Missing Scopes: {missing_scopes}")
            print(f"Screenshots at: {SCREENSHOT_DIR}")
            print(f"Credentials file: {CREDENTIALS_FILE}")

        except Exception as e:
            print(f"\nERROR: {e}")
            import traceback
            traceback.print_exc()
            wait_and_screenshot(page, "ERROR_state")
        finally:
            browser.close()


if __name__ == "__main__":
    main()
