#!/usr/bin/env python3
"""
Kiwi Browser API v2 — Playwright-basiert mit echten Maus-Klicks.
Läuft im Docker-Container auf ZimaOS.

Endpunkte:
    GET  /health              → Status
    POST /visit               → Seite laden + Text extrahieren + Screenshot
    POST /screenshot          → Screenshot als Base64
    POST /click               → Maus-Klick auf Element (CSS-Selektor oder x,y)
    POST /type                → Text in Input-Feld tippen
    POST /scroll              → Scrollen (down/up/to)
    POST /evaluate            → JavaScript im Browser ausführen
    POST /navigate            → Zu URL navigieren + warten
"""

import asyncio, json, base64, os, re
from aiohttp import web
from playwright.async_api import async_playwright

PORT = int(os.environ.get("PORT", 3000))

class BrowserPool:
    def __init__(self):
        self._playwright = None
        self._browser = None
        self._lock = asyncio.Lock()

    async def start(self):
        self._playwright = await async_playwright().start()
        self._browser = await self._playwright.chromium.launch(
            headless=True,
            args=[
                "--no-sandbox",
                "--disable-gpu",
                "--disable-dev-shm-usage",
                "--disable-background-networking",
                "--disable-background-timer-throttling",
                "--disable-backgrounding-occluded-windows",
                "--disable-renderer-backgrounding",
                "--disable-features=IsolateOrigins,site-per-process",
                "--disable-blink-features=AutomationControlled",
                "--window-size=1920,1080",
            ]
        )

    async def new_page(self):
        ctx = await self._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/120.0.0.0 Safari/537.36",
            locale="de-DE",
            timezone_id="Europe/Vienna",
        )
        page = await ctx.new_page()
        # Anti-Detection Script
        await page.add_init_script("""
            Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
            Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3,4,5]});
            window.chrome = { runtime: {} };
            Object.defineProperty(navigator, 'languages', {get: () => ['de-DE', 'de', 'en-US', 'en']});
        """)
        return page, ctx

    async def stop(self):
        if self._browser:
            await self._browser.close()
        if self._playwright:
            await self._playwright.stop()

pool = BrowserPool()

# ── Helpers ──

def json_response(data, status=200):
    return web.json_response(data, status=status)

async def safe_goto(page, url, wait_until="networkidle", timeout=30000):
    try:
        await page.goto(url, wait_until=wait_until, timeout=timeout)
        return True
    except Exception as e:
        return str(e)

async def page_to_screenshot_base64(page, full_page=False):
    img_bytes = await page.screenshot(full_page=full_page, type="png")
    return base64.b64encode(img_bytes).decode()

# ── Handlers ──

async def health(request):
    return json_response({"status": "ok", "version": "v2-playwright"})

async def visit(request):
    data = await request.json()
    url = data.get("url", "https://example.com")
    selector = data.get("selector")
    wait = data.get("wait", "networkidle")
    screenshot = data.get("screenshot", False)

    page, ctx = await pool.new_page()
    try:
        ok = await safe_goto(page, url, wait_until=wait)
        if ok is not True:
            return json_response({"success": False, "error": ok}, 500)

        title = await page.title()
        html = await page.content()

        extracted = None
        if selector:
            try:
                el = await page.query_selector(selector)
                if el:
                    extracted = await el.text_content()
                    if extracted:
                        extracted = extracted.strip()
            except:
                pass

        result = {
            "success": True,
            "url": url,
            "title": title,
            "html_preview": html[:3000],
            "extracted": extracted,
        }

        if screenshot:
            result["screenshot_base64"] = await page_to_screenshot_base64(page, full_page=data.get("full_page", False))

        return json_response(result)
    finally:
        await ctx.close()

async def screenshot(request):
    data = await request.json()
    url = data.get("url", "https://example.com")
    full_page = data.get("full_page", False)
    wait = data.get("wait", "networkidle")

    page, ctx = await pool.new_page()
    try:
        ok = await safe_goto(page, url, wait_until=wait)
        if ok is not True:
            return json_response({"success": False, "error": ok}, 500)
        b64 = await page_to_screenshot_base64(page, full_page=full_page)
        return json_response({"success": True, "screenshot_base64": b64, "url": url})
    finally:
        await ctx.close()

async def click(request):
    """Echter Maus-Klick auf Element (CSS-Selektor) oder Koordinaten (x,y)."""
    data = await request.json()
    url = data.get("url")
    selector = data.get("selector")
    x = data.get("x")
    y = data.get("y")
    wait_for = data.get("wait_for")
    screenshot = data.get("screenshot", False)

    page, ctx = await pool.new_page()
    try:
        if url:
            ok = await safe_goto(page, url)
            if ok is not True:
                return json_response({"success": False, "error": ok}, 500)
            if wait_for:
                await page.wait_for_selector(wait_for, timeout=10000)

        if selector:
            await page.click(selector, timeout=10000)
            clicked = selector
        elif x is not None and y is not None:
            await page.mouse.click(x, y)
            clicked = f"x={x}, y={y}"
        else:
            return json_response({"success": False, "error": "selector or x,y required"}, 400)

        # Kurze Pause für Page-Transitionen
        await asyncio.sleep(0.5)

        result = {
            "success": True,
            "clicked": clicked,
            "current_url": page.url,
            "title": await page.title(),
        }
        if screenshot:
            result["screenshot_base64"] = await page_to_screenshot_base64(page)
        return json_response(result)
    except Exception as e:
        return json_response({"success": False, "error": str(e)}, 500)
    finally:
        await ctx.close()

async def type_text(request):
    """Text in ein Input-Feld tippen."""
    data = await request.json()
    url = data.get("url")
    selector = data.get("selector")
    text = data.get("text", "")
    submit = data.get("submit", False)

    page, ctx = await pool.new_page()
    try:
        if url:
            ok = await safe_goto(page, url)
            if ok is not True:
                return json_response({"success": False, "error": ok}, 500)

        if not selector:
            return json_response({"success": False, "error": "selector required"}, 400)

        await page.fill(selector, text)
        if submit:
            await page.press(selector, "Enter")
            await asyncio.sleep(0.5)

        return json_response({
            "success": True,
            "selector": selector,
            "text": text,
            "current_url": page.url,
            "title": await page.title(),
        })
    except Exception as e:
        return json_response({"success": False, "error": str(e)}, 500)
    finally:
        await ctx.close()

async def scroll(request):
    """Scrollen: {"direction": "down|up|bottom|top", "amount": 500} oder {"to": "selector"}."""
    data = await request.json()
    url = data.get("url")
    direction = data.get("direction", "down")
    amount = data.get("amount", 500)
    to_selector = data.get("to")

    page, ctx = await pool.new_page()
    try:
        if url:
            ok = await safe_goto(page, url)
            if ok is not True:
                return json_response({"success": False, "error": ok}, 500)

        if to_selector:
            await page.evaluate(f"document.querySelector('{to_selector}').scrollIntoView()")
        else:
            if direction == "down":
                await page.evaluate(f"window.scrollBy(0, {amount})")
            elif direction == "up":
                await page.evaluate(f"window.scrollBy(0, -{amount})")
            elif direction == "bottom":
                await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
            elif direction == "top":
                await page.evaluate("window.scrollTo(0, 0)")

        await asyncio.sleep(0.3)

        return json_response({
            "success": True,
            "current_url": page.url,
            "scroll_y": await page.evaluate("window.scrollY"),
        })
    except Exception as e:
        return json_response({"success": False, "error": str(e)}, 500)
    finally:
        await ctx.close()

async def evaluate(request):
    """JavaScript im Browser ausführen."""
    data = await request.json()
    url = data.get("url")
    script = data.get("script", "")

    page, ctx = await pool.new_page()
    try:
        if url:
            ok = await safe_goto(page, url)
            if ok is not True:
                return json_response({"success": False, "error": ok}, 500)
        result = await page.evaluate(script)
        return json_response({"success": True, "result": result, "current_url": page.url})
    except Exception as e:
        return json_response({"success": False, "error": str(e)}, 500)
    finally:
        await ctx.close()

async def navigate(request):
    data = await request.json()
    url = data.get("url", "https://example.com")
    wait = data.get("wait", "networkidle")

    page, ctx = await pool.new_page()
    try:
        ok = await safe_goto(page, url, wait_until=wait)
        if ok is not True:
            return json_response({"success": False, "error": ok}, 500)
        return json_response({
            "success": True,
            "url": page.url,
            "title": await page.title(),
        })
    finally:
        await ctx.close()

# ── App ──

app = web.Application()
app.router.add_get("/health", health)
app.router.add_post("/visit", visit)
app.router.add_post("/screenshot", screenshot)
app.router.add_post("/click", click)
app.router.add_post("/type", type_text)
app.router.add_post("/scroll", scroll)
app.router.add_post("/evaluate", evaluate)
app.router.add_post("/navigate", navigate)

async def on_startup(app):
    await pool.start()
    print(f"Kiwi Browser v2 running on http://0.0.0.0:{PORT}", flush=True)

async def on_cleanup(app):
    await pool.stop()

app.on_startup.append(on_startup)
app.on_cleanup.append(on_cleanup)

if __name__ == "__main__":
    web.run_app(app, host="0.0.0.0", port=PORT)
