#!/usr/bin/env python3
"""
E2E test: visit every URL, screenshot, collect errors.
Generates JSON results + report-ready data.
"""
import asyncio, json, os, re
from datetime import datetime
from playwright.async_api import async_playwright

BASE = "https://sj88ai.com"
REPORT_DIR = "/workspace/sj88auth/reports/2026-07-12-block-cms"
SCREENSHOTS = f"{REPORT_DIR}/screenshots"
MOBILE_SHOTS = f"{REPORT_DIR}/mobile-screenshots"
API_EVIDENCE = f"{REPORT_DIR}/api-evidence"
HTML_SOURCE = f"{REPORT_DIR}/html-source"

# Pages to test — categorized
PAGES = [
    # === Marketing / course pages ===
    ("home",                       "GET",  "/",                                                            "public", True),
    ("shopee-salepage",            "GET",  "/courses/shopee-affiliate/",                                  "public", True),
    ("shopee-lessons",             "GET",  "/courses/shopee-affiliate/lessons.html",                      "public", True),
    ("shopee-checkout",            "GET",  "/courses/shopee-affiliate/checkout.html",                     "public", True),
    ("tiktok-ai-lessons",          "GET",  "/courses/tiktok-ai-2026/lessons.html",                       "public", True),
    ("tiktok-sulatan-lessons",     "GET",  "/courses/tiktok-sulatan-2026/lessons.html",                  "public", True),
    ("vibecode-lessons",           "GET",  "/courses/vibecode-101/lessons.html",                          "public", True),

    # === Sample published content pages ===
    ("shopee-ep0-full",            "GET",  "/shopee-affiliate/ep0.html",                                  "public", True),
    ("shopee-lesson-3",            "GET",  "/shopee-affiliate/lesson-3.html",                             "public", True),
    ("shopee-tool-1",              "GET",  "/shopee-affiliate/tool-1.html",                               "public", True),
    ("tiktok-ai-intro",            "GET",  "/tiktok-ai-2026/intro.html",                                  "public", True),
    ("tiktok-sulatan-ep0",         "GET",  "/tiktok-sulatan-2026/ep0.html",                               "public", True),
    ("vibecode-ep1",               "GET",  "/vibecode-101/ep1.html",                                      "public", True),

    # === Auth pages ===
    ("signup",                     "GET",  "/signup.html",                                                "public", True),
    ("login",                      "GET",  "/login.html",                                                 "public", True),
    ("account",                    "GET",  "/account.html",                                               "auth", True),

    # === Admin (editor) ===
    ("editor",                     "GET",  "/editor.html",                                                "auth", True),

    # === API endpoints ===
    ("api-health",                 "GET",  "/auth/health",                                                "api", False),
    ("api-list-shopee",            "GET",  "/content/list?course=shopee-affiliate&status=published",      "api", False),
    ("api-list-tiktok-ai",         "GET",  "/content/list?course=tiktok-ai-2026&status=published",        "api", False),
    ("api-list-tiktok-sulatan",    "GET",  "/content/list?course=tiktok-sulatan-2026&status=published",   "api", False),
    ("api-list-vibecode",          "GET",  "/content/list?course=vibecode-101&status=published",         "api", False),

    # === Old removed pages (should be 404) ===
    ("old-curriculum",             "GET",  "/courses/shopee-affiliate/curriculum.html",                   "expected_404", False),
    ("old-preview",                "GET",  "/courses/shopee-affiliate/preview.html",                      "expected_404", False),
    ("old-tools",                  "GET",  "/courses/shopee-affiliate/tools.html",                        "expected_404", False),
]

async def test_page(page, name, method, path, category, screenshot):
    """Test single page: load, check for errors, screenshot"""
    url = BASE + path
    result = {
        "name": name, "path": path, "category": category, "url": url,
        "status": None, "ok": None, "errors": [], "warnings": [],
        "load_time_ms": None, "size_bytes": None, "screenshot": None, "mobile_screenshot": None,
        "html_excerpt": None, "console_errors": []
    }

    # Capture console errors
    console_errors = []
    page.on("console", lambda msg: console_errors.append(f"{msg.type}: {msg.text}") if msg.type in ("error", "warning") else None)
    page.on("pageerror", lambda err: console_errors.append(f"pageerror: {err.message}"))

    start = asyncio.get_event_loop().time()
    try:
        response = await page.goto(url, wait_until="networkidle", timeout=30000)
        elapsed = (asyncio.get_event_loop().time() - start) * 1000
        result["load_time_ms"] = round(elapsed, 1)
        result["status"] = response.status if response else None
        try:
            body = await response.body() if response else b""
            result["size_bytes"] = len(body)
        except Exception:
            result["size_bytes"] = None

        if category == "expected_404":
            result["ok"] = (result["status"] == 404)
        else:
            result["ok"] = (result["status"] == 200)

        if screenshot and result["ok"]:
            # Desktop full page
            shot_path = f"{SCREENSHOTS}/{name}.png"
            await page.screenshot(path=shot_path, full_page=True)
            result["screenshot"] = shot_path
            result["screenshot_size"] = os.path.getsize(shot_path)

            # Mobile
            mobile_path = f"{MOBILE_SHOTS}/{name}.png"
            await page.set_viewport_size({"width": 390, "height": 844})
            await page.screenshot(path=mobile_path, full_page=False)  # viewport only for mobile
            result["mobile_screenshot"] = mobile_path
            result["mobile_screenshot_size"] = os.path.getsize(mobile_path)
            await page.set_viewport_size({"width": 1280, "height": 800})  # reset

            # Save HTML excerpt
            html = await page.content()
            with open(f"{HTML_SOURCE}/{name}.html", "w", encoding="utf-8") as f:
                f.write(html)
            result["html_size"] = len(html)
            result["html_excerpt"] = html[:500]

        result["console_errors"] = console_errors
        if console_errors:
            result["warnings"].extend([e for e in console_errors if "warning" in e.lower()])
            result["errors"].extend([e for e in console_errors if "error" in e.lower()])

        # Check for Thai text
        if category == "public" and result["ok"]:
            content = await page.content()
            has_thai = bool(re.search(r'[ก-๛]', content))
            result["has_thai"] = has_thai
            if not has_thai and 'login' not in name and 'signup' not in name and 'health' not in name:
                result["warnings"].append("No Thai characters found on page")

        # Check for Vibe theme
        if category == "public" and result["ok"]:
            content = await page.content()
            has_vibe = '#ec4899' in content or '--pink' in content or 'Fraunces' in content
            result["has_vibe"] = has_vibe

    except Exception as e:
        result["errors"].append(f"Exception: {str(e)}")
        result["ok"] = False

    return result

async def test_api_endpoint(context, name, method, path):
    """Test API endpoint via HTTP, save raw response"""
    url = BASE + path
    result = {"name": name, "path": path, "ok": None, "status": None, "size": None, "sample": None}
    try:
        if method == "GET":
            resp = await context.request.get(url)
        result["status"] = resp.status
        result["ok"] = (resp.status == 200)
        text = await resp.text()
        result["size"] = len(text)
        result["sample"] = text[:500]
        # Save full response
        with open(f"{API_EVIDENCE}/{name}.json", "w", encoding="utf-8") as f:
            try:
                f.write(json.dumps(json.loads(text), indent=2, ensure_ascii=False))
            except:
                f.write(text)
    except Exception as e:
        result["error"] = str(e)
        result["ok"] = False
    return result

async def main():
    print(f"=== E2E test at {datetime.now().isoformat()} ===")
    results = []

    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        # Desktop context
        ctx_desktop = await browser.new_context(viewport={"width": 1280, "height": 800})
        page_desktop = await ctx_desktop.new_page()
        # Mobile context
        ctx_mobile = await browser.new_context(
            viewport={"width": 390, "height": 844},
            user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
            device_scale_factor=2, is_mobile=True
        )

        for name, method, path, category, screenshot in PAGES:
            print(f"  Testing {name} ({path})...")
            if category == "api":
                r = await test_api_endpoint(ctx_desktop, name, method, path)
            else:
                r = await test_page(page_desktop, name, method, path, category, screenshot)
            results.append(r)
            status_icon = "✓" if r["ok"] else "✗"
            print(f"    {status_icon} {r.get('status', '?')} {r.get('size_bytes', r.get('size', '?'))} bytes | {len(r.get('errors', []))} errors")

        # Test interactions: click nav, scroll, test form
        print("\n=== Interaction tests ===")
        interaction_results = await test_interactions(page_desktop)
        results.extend(interaction_results)

        await browser.close()

    # Save raw results
    with open(f"{REPORT_DIR}/test-results.json", "w", encoding="utf-8") as f:
        json.dump(results, f, indent=2, ensure_ascii=False)

    # Summary
    total = len(results)
    passed = sum(1 for r in results if r.get("ok"))
    print(f"\n=== Summary: {passed}/{total} passed ===")
    return results

async def test_interactions(page):
    """Test specific user flows"""
    interactions = []

    # 1) Home → click Shopee card
    print("  [1] Home → Shopee card click...")
    try:
        await page.goto(BASE + "/", wait_until="networkidle", timeout=30000)
        await page.click("text=Shopee Affiliate", timeout=10000)
        await page.wait_for_url("**/courses/shopee-affiliate/**", timeout=10000)
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-1-home-to-shopee.png", full_page=True)
        interactions.append({
            "name": "interaction-home-to-shopee", "category": "interaction",
            "ok": True, "path": "/courses/shopee-affiliate/"
        })
    except Exception as e:
        interactions.append({
            "name": "interaction-home-to-shopee", "category": "interaction",
            "ok": False, "error": str(e)
        })

    # 2) Salepage → click "ดูบทเรียนทั้งหมด" → lessons page
    print("  [2] Salepage → lessons page...")
    try:
        await page.goto(BASE + "/courses/shopee-affiliate/", wait_until="networkidle", timeout=30000)
        await page.click("text=ดูบทเรียนทั้งหมด", timeout=10000)
        await page.wait_for_url("**/lessons.html", timeout=10000)
        await page.wait_for_timeout(2000)  # let API load
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-2-lessons.png", full_page=True)
        # Check that lesson cards loaded
        cards = await page.query_selector_all(".lesson-card")
        interactions.append({
            "name": "interaction-salepage-to-lessons", "category": "interaction",
            "ok": True, "path": page.url,
            "cards_loaded": len(cards)
        })
    except Exception as e:
        interactions.append({
            "name": "interaction-salepage-to-lessons", "category": "interaction",
            "ok": False, "error": str(e)
        })

    # 3) Lessons page → click an Ep card → see static content
    print("  [3] Lessons page → Ep0 click...")
    try:
        first_card = await page.query_selector(".lesson-card.featured")
        if first_card:
            await first_card.click()
            await page.wait_for_url("**/ep0.html", timeout=10000)
            await page.wait_for_timeout(1000)
            await page.screenshot(path=f"{SCREENSHOTS}/interaction-3-ep0.png", full_page=True)
            # Verify content
            h1 = await page.query_selector("h1.content-h1")
            title = await h1.text_content() if h1 else None
            interactions.append({
                "name": "interaction-lessons-to-ep0", "category": "interaction",
                "ok": True, "path": page.url,
                "h1": title
            })
    except Exception as e:
        interactions.append({
            "name": "interaction-lessons-to-ep0", "category": "interaction",
            "ok": False, "error": str(e)
        })

    # 4) Editor login flow
    print("  [4] Editor page → login flow...")
    try:
        await page.goto(BASE + "/editor.html", wait_until="networkidle", timeout=30000)
        await page.wait_for_timeout(1000)
        # Should redirect to login if not logged in
        url = page.url
        interactions.append({
            "name": "interaction-editor-redirects", "category": "interaction",
            "ok": "login" in url or "signup" in url,
            "redirected_to": url
        })
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-4-editor-redirect.png", full_page=False)
    except Exception as e:
        interactions.append({
            "name": "interaction-editor-redirects", "category": "interaction",
            "ok": False, "error": str(e)
        })

    # 5) Login → editor → see content list
    print("  [5] Login → editor → content list...")
    try:
        # Signup a new test user
        import time
        test_email = f"e2e-test-{int(time.time())}@sj88ai.com"
        await page.goto(BASE + "/signup.html", wait_until="networkidle", timeout=30000)
        await page.fill('input[type="email"]', test_email)
        await page.fill('input[type="password"]', "test12345")
        await page.fill('input[name="name"], input[autocomplete="name"]', "E2E Test User")
        await page.click('button[type="submit"]')
        await page.wait_for_url("**/account.html", timeout=10000)
        await page.wait_for_timeout(2000)
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-5-after-signup.png", full_page=True)
        # Go to editor
        await page.goto(BASE + "/editor.html", wait_until="networkidle", timeout=30000)
        await page.wait_for_timeout(3000)  # wait for content list
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-5-editor.png", full_page=True)
        # Check if content list visible
        items = await page.query_selector_all(".content-list-item")
        interactions.append({
            "name": "interaction-editor-with-content", "category": "interaction",
            "ok": True, "path": page.url,
            "content_items": len(items)
        })
    except Exception as e:
        interactions.append({
            "name": "interaction-editor-with-content", "category": "interaction",
            "ok": False, "error": str(e)
        })

    # 6) Checkout form
    print("  [6] Checkout form...")
    try:
        await page.goto(BASE + "/courses/shopee-affiliate/checkout.html", wait_until="networkidle", timeout=30000)
        await page.wait_for_timeout(1000)
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-6-checkout.png", full_page=True)
        # Try to switch pay method
        await page.click('input[value="prompt"]')
        await page.wait_for_timeout(500)
        bank_details_visible = await page.is_visible("#bank-details")
        interactions.append({
            "name": "interaction-checkout-pay-toggle", "category": "interaction",
            "ok": not bank_details_visible,  # should hide when prompt selected
            "bank_hidden": not bank_details_visible
        })
        await page.screenshot(path=f"{SCREENSHOTS}/interaction-6-checkout-prompt.png", full_page=True)
    except Exception as e:
        interactions.append({
            "name": "interaction-checkout-pay-toggle", "category": "interaction",
            "ok": False, "error": str(e)
        })

    return interactions

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