#!/usr/bin/env python3
"""
GHL Skill: Extract API Key
==========================
Navigates to GHL Agency Settings and copies the API Key.

VERIFICATION_STAMP
Story: UVS-H03
Verified By: Claude Opus 4.5
Verified At: 2026-02-03
Security: Selector allowlist validation, exfiltration prevention
"""

from core.skills.ghl_base import GHLSkill, registry
from core.browser_controller import NavigationStatus
from core.security.selector_sanitizer import (
    sanitize_selector,
    validate_selector,
    SelectorValidationError,
    GHL_SELECTOR_ALLOWLIST
)
import asyncio
import json
import logging

logger = logging.getLogger(__name__)

# UVS-H03: Allowlist of safe selectors for API key extraction
# Only these patterns can be used to query for API keys
API_KEY_SELECTOR_ALLOWLIST = [
    r'^input\[name=["\']apiKey["\']\]$',      # input[name='apiKey']
    r'^#api-key-value$',                       # #api-key-value
    r'^input\[data-testid=["\'][\w-]+["\']\]$', # input[data-testid="..."]
    r'^\[data-key-input\]$',                   # [data-key-input]
    r'^\.api-key-input$',                      # .api-key-input
]


def validate_api_key_selector(selector: str) -> bool:
    """
    Validates a selector is safe for API key extraction.

    Rejects any selector that could be used for data exfiltration:
    - fetch(), XMLHttpRequest
    - eval(), Function()
    - document.* manipulation
    - Event handlers

    Args:
        selector: CSS selector from knowledge graph

    Returns:
        True if selector is safe, False otherwise
    """
    try:
        # First run through general sanitizer to block XSS patterns
        sanitize_selector(selector)

        # Then validate against API key allowlist
        valid, reason = validate_selector(selector, API_KEY_SELECTOR_ALLOWLIST)
        if not valid:
            logger.warning(f"[SECURITY] API key selector rejected: {selector} - {reason}")
            return False

        return True
    except SelectorValidationError as e:
        logger.warning(f"[SECURITY] Dangerous API key selector blocked: {selector} - {e}")
        return False


class ExtractAPIKeySkill(GHLSkill):
    @property
    def name(self) -> str:
        return "extract_api_key"

    @property
    def description(self) -> str:
        return "Navigate to Agency Settings and extract the GHL API Key."

    @property
    def parameters(self) -> dict:
        return {
            "type": "OBJECT",
            "properties": {},
            "required": []
        }

    async def execute(self, controller, **kwargs):
        logger.info("Executing production ExtractAPIKeySkill...")
        await self.update_progress(controller, "Navigating to Agency Settings...")

        # 1. Navigate via Synergy Bridge (Informed Navigation)
        endpoint = controller.ghl_bridge.get_api_endpoint("Get API Key") or "/settings/api_keys"
        url = f"https://app.gohighlevel.com{endpoint}" if not endpoint.startswith("http") else endpoint

        result = await controller.navigate(url)
        if result.status != NavigationStatus.SUCCESS:
            return {"status": "error", "message": f"Navigation failed: {result.status.value}"}

        await asyncio.sleep(2) # Stabilize

        await self.update_progress(controller, "Locating API Key...")

        # 2. Extract using Knowledge Graph selector with security validation (UVS-H03)
        kb_selector = controller.ghl_bridge.get_selector("agency_api_key_input")

        # Default safe selector
        selector = "input[name='apiKey']"

        # Only use KB selector if it passes security validation
        if kb_selector:
            if validate_api_key_selector(kb_selector):
                selector = kb_selector
            else:
                logger.warning(f"[SECURITY] KB selector failed validation, using default: {kb_selector}")

        # Wait for element visibility and value - use safe parameterized query
        for _ in range(5):
            # UVS-H03: Safe parameterized selector query
            safe_selector = json.dumps(selector)
            val = await controller.evaluate(f"document.querySelector({safe_selector})?.value")
            if val and len(val) > 10: # Likely a real key
                await self.update_progress(controller, "API Key Extracted Successfully.")
                await asyncio.sleep(1)
                await self.update_progress(controller, None)
                return {"status": "success", "api_key": val}
            await asyncio.sleep(1)

        await self.update_progress(controller, "Extraction failed. Retrying with fallback...")
        
        # Fallback heuristic
        fallback_script = """
            (() => {
                const apiInput = document.querySelector('input[name="apiKey"]') || 
                               document.querySelector('#api-key-value') ||
                               Array.from(document.querySelectorAll('input')).find(i => i.value.length > 20 && /^[a-f0-9-]+$/i.test(i.value));
                return apiInput ? apiInput.value : null;
            })()
        """
        api_key = await controller.evaluate(fallback_script)
        
        await self.update_progress(controller, None)
        
        if api_key:
            return {"status": "success", "api_key": api_key}
        else:
            return {"status": "error", "message": "Could not extract API key. Ensure you are in Agency Settings with valid permissions."}

# Automatically register the skill
registry.register(ExtractAPIKeySkill())
