"""E2E smoke test for 1v9 â€” Convertite en Leyenda.

Juega carreras completas con un bot y verifica:
- 0 errores de consola / 0 requests fallidos
- resumen alcanzado en 100% de las carreras
- sin overflow horizontal (desktop + mobile)
- timeline colapsada en mobile
- design tokens correctos
- gating: 2Âª divisiÃ³n no ve competencias internacionales
- fichajes solo desde split >= 4
- stats sin NaN ni claves fantasma
- diversidad: las 5 competencias aparecen en N carreras
- export de imagen 1080x1920 vÃ¡lido (PIL)
"""
import asyncio
import json
import sys
import random

from playwright.async_api import async_playwright

BASE = "http://127.0.0.1:8899/"


async def pick_region_role(page):
    cards = page.locator(".region-card")
    n = await cards.count()
    idx = random.randrange(n)
    await cards.nth(idx).click()
    cards = page.locator(".role-card")
    n = await cards.count()
    await cards.nth(random.randrange(n)).click()


async def play_career(page, want_export=False, max_rounds=60):
    """Juega una carrera completa. Devuelve dict con datos de la partida."""
    data = {"decisions": [], "intl_seen": [], "categories": [], "transfer_splits": [], "errors": [], "intl_divs": []}
    page.on("pageerror", lambda e: data["errors"].append(f"pageerror: {e}"))
    page.on("console", lambda m: data["errors"].append(f"console: {m.text}") if m.type == "error" else None)
    page.on("requestfailed", lambda r: data["errors"].append(f"requestfailed: {r.url}"))

    await page.goto(BASE, wait_until="networkidle")
    await page.click("#btnStart")
    await pick_region_role(page)
    await page.fill("#inpNick", "BotPro")
    await page.click("#btnBegin")

    await page.click(".opt-card:visible >> nth=0")  # oferta de academia

    rounds = 0
    while rounds < max_rounds:
        rounds += 1
        summary_active = await page.evaluate("document.getElementById('summary').classList.contains('active')")
        if summary_active:
            break
        overlay = await page.evaluate("document.getElementById('resultOverlay').classList.contains('show')")
        if overlay:
            cont = page.locator("#btnContinue")
            if await cont.count():
                await cont.click()
                continue
        # nueva decisiÃ³n renderizada
        cat = await page.locator(".dc-cat").first.text_content()
        if cat:
            data["categories"].append(cat.strip())
            if "Fichaje" in cat:
                split = await page.evaluate("G.split")
                data["transfer_splits"].append(split)
            for comp in ["First Stand", "MSI", "EWC", "Asian Games", "Worlds"]:
                if comp in cat and comp not in data["intl_seen"]:
                    data["intl_seen"].append(comp)
            if any(c in cat for c in ["First Stand", "MSI", "EWC", "Asian Games", "Worlds"]):
                div = await page.evaluate("G.div")
                data["intl_divs"].append(div)
        opts = page.locator(".opt-card:visible")
        n = await opts.count()
        if not n:
            await page.wait_for_timeout(300)
            continue
        # 40% de retirarse cuando estÃ© disponible (split >= 6) para acelerar
        retire = page.locator('.opt-card[data-i="retire"]:visible')
        if await retire.count() and random.random() < 0.4:
            await retire.click()
        else:
            await opts.nth(random.randrange(n)).click()
        await page.wait_for_timeout(120)

    state = await page.evaluate("""() => ({
      split: G.split, totalSplits: G.totalSplits, retired: G.retired, div: G.div,
      stats: G.stats, teamId: G.teamId, intl: G.intl, teamTitles: G.teamTitles,
      indTitles: G.indTitles, history: G.history, games: G.games,
      kills: G.kills, assists: G.assists, deaths: G.deaths, penta: G.penta,
      maxValue: G.maxValue, ratingMax: G.ratingMax, regSelect: G.regSelect
    })""")
    data["state"] = state

    if want_export:
        async with page.expect_download(timeout=10000) as dl_info:
            await page.click("#btnExport")
        dl = await dl_info.value
        path = await dl.path()
        data["download"] = path

    return data


async def check_layout(page, is_mobile):
    """Chequeos de layout programáticos."""
    res = {}
    res["overflow"] = await page.evaluate(
        "document.documentElement.scrollWidth > window.innerWidth + 1"
    )
    res["tl_closed_mobile"] = await page.evaluate(
        """() => {
          const b = document.getElementById('tlBody');
          return !b || b.classList.contains('closed');
        }"""
    ) if is_mobile else None
    res["tokens"] = await page.evaluate(
        """() => {
          const s = getComputedStyle(document.documentElement);
          return {
            accent: s.getPropertyValue('--accent-ovr').trim(),
            success: s.getPropertyValue('--success').trim(),
            bg: s.getPropertyValue('--bg-base').trim()
          };
        }"""
    )
    return res


def check_state(data):
    """Verifica mecÃ¡nicas sobre el estado final de una carrera."""
    issues = []
    st = data["state"]
    for k, v in st["stats"].items():
        if v != v or v is None:  # NaN check
            issues.append(f"stat NaN: {k}")
    ghost = [k for k in st["stats"] if k in ("valor",)]
    if ghost:
        issues.append(f"ghost stats: {ghost}")
    # gating internacionales: evento intl solo con div 1
    for cat in data["categories"]:
        if any(c in cat for c in ["First Stand", "MSI", "EWC", "Asian Games", "Worlds"]):
            pass  # no podemos saber el div en el momento; se chequea en vivo abajo
    # fichajes >= split 4
    for s in data["transfer_splits"]:
        if s < 4:
            issues.append(f"transfer en split {s} (< 4)")
    # gating: toda competencia vista con div 1
    for d in data["intl_divs"]:
        if d != 1:
            issues.append(f"competencia internacional vista en div {d}")
    return issues


def validate_export(path):
    """Valida el PNG exportado con PIL."""
    try:
        from PIL import Image
    except ImportError:
        return "PIL no instalado (skip)"
    img = Image.open(path)
    size = img.size
    mode = img.mode
    px = img.convert("RGB").getpixel((100, 100))
    luma = img.convert("L").getextrema()
    ok = size == (1080, 1920) and luma[1] > 60
    return f"OK size={size} mode={mode} bg={px} luma={luma}" if ok else f"FAIL size={size} mode={mode} bg={px} luma={luma}"


async def main():
    results = {"errors_total": 0, "careers": [], "intl_all": set(), "export_ok": None}
    n_careers = int(sys.argv[1]) if len(sys.argv) > 1 else 6

    async with async_playwright() as p:
        browser = await p.chromium.launch()

        # --- desktop ---
        page = await browser.new_page(viewport={"width": 1280, "height": 800})
        data = await play_career(page, want_export=True)
        layout = await check_layout(page, is_mobile=False)
        print("DESKTOP layout:", json.dumps(layout, ensure_ascii=False))
        print("DESKTOP errors:", data["errors"])
        print("DESKTOP finished:", data["state"]["split"] >= data["state"]["totalSplits"] or data["state"]["retired"])
        if data.get("download"):
            results["export_ok"] = validate_export(data["download"])
            print("DESKTOP export:", results["export_ok"])
        await page.close()

        # --- mobile (390x844) + N carreras ---
        for i in range(n_careers):
            page = await browser.new_page(viewport={"width": 390, "height": 844})
            data = await play_career(page, want_export=False)
            issues = check_state(data)
            finished = data["state"]["split"] >= data["state"]["totalSplits"] or data["state"]["retired"]
            print(f"--- carrera {i+1}: errors={len(data['errors'])} finished={finished} div={data['state']['div']} intl={data['intl_seen']} transfers={data['transfer_splits']} issues={issues}")
            results["errors_total"] += len(data["errors"])
            results["careers"].append({"finished": finished, "issues": issues})
            results["intl_all"] |= set(data["intl_seen"])
            await page.close()

        # layout mobile una vez
        page = await browser.new_page(viewport={"width": 390, "height": 844})
        data = await play_career(page)
        layout = await check_layout(page, is_mobile=True)
        print("MOBILE layout:", json.dumps(layout, ensure_ascii=False))
        await page.close()
        await browser.close()

    ok = results["errors_total"] == 0 and all(c["finished"] for c in results["careers"])
    print("\n=== RESUMEN ===")
    print("errores totales:", results["errors_total"])
    print("carreras completadas:", sum(1 for c in results["careers"] if c["finished"]), "/", len(results["careers"]))
    print("issue por carrera:", [c["issues"] for c in results["careers"]])
    print("competencias vistas:", sorted(results["intl_all"]))
    print("export:", results["export_ok"])
    return 0 if ok else 1


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

