"""Diff de uma ou mais URLs entre PHP 7.4 (base74) e PHP 8.3 (alvo83), com o navegador renderizando.

Para cada URL: HTML renderizado (page.content() apos load) normalizado, texto visivel (inner_text do body),
tags <script src>, <link href>, <input name>, <select name>, <textarea name>, erros de console/pageerror e
respostas HTTP >= 400 (para descobrir qual recurso deu 404). Mostra diff unificado resumido de cada parte.

Uso:
  export ACCIA_VAL_PASS=...
  python diff_html.py /helper/faq-create /termos-uso/aceite [--salvar DIR] [--max 60]

ATENCAO: so faz GET, mas algumas acoes do sistema gravam no banco em GET (ex.: /performance/vertical/copia,
/performance/vertical/duplica-cluster). Nao passe essas URLs.
"""
import argparse
import difflib
import re
import sys
from pathlib import Path

from config import AMBIENTES
from crawl import _login
from normaliza import normalizar_html

_DATA = re.compile(r"\b\d{2}/\d{2}/\d{4}(\s+\d{2}:\d{2}(:\d{2})?)?|\b\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?)?")

_JS_TAGS = """() => {
  const q = (sel, at) => Array.from(document.querySelectorAll(sel)).map(e => sel.split('[')[0] + ' ' + at + '=' + e.getAttribute(at));
  return [].concat(
    q('script[src]', 'src'), q('link[href]', 'href'),
    q('input[name]', 'name'), q('select[name]', 'name'), q('textarea[name]', 'name'));
}"""


def _coletar(page, base_url, url, espera=1500):
    erros, falhas = [], []

    def on_console(m):
        if m.type == "error":
            erros.append(m.text[:300])

    def on_pageerror(e):
        erros.append(("pageerror: " + str(e))[:300])

    def on_response(r):
        if r.status >= 400:
            falhas.append(f"{r.status} {r.request.method} {r.url}")

    def on_failed(r):
        falhas.append(f"FALHOU {r.method} {r.url} ({r.failure})")

    page.on("console", on_console)
    page.on("pageerror", on_pageerror)
    page.on("response", on_response)
    page.on("requestfailed", on_failed)
    res = {"status": None, "final": None, "html": "", "texto": "", "tags": [], "erros": erros, "falhas": falhas}
    try:
        resp = page.goto(base_url + url, wait_until="load", timeout=90000)
        page.wait_for_timeout(espera)  # deixa widgets JS (editores, select2, mascaras) terminarem
        res["status"] = resp.status if resp else None
        res["final"] = page.url.replace(base_url, "")
        res["html"] = page.content()
        res["texto"] = page.inner_text("body")
        res["tags"] = page.evaluate(_JS_TAGS)
    except Exception as e:
        res["erros"].append("excecao: " + str(e).splitlines()[0][:300])
    finally:
        for ev, fn in (("console", on_console), ("pageerror", on_pageerror), ("response", on_response),
                       ("requestfailed", on_failed)):
            page.remove_listener(ev, fn)
    return res


def _linhas_texto(t):
    t = _DATA.sub("~data~", t)
    return [ln.strip() for ln in t.splitlines() if ln.strip()]


def _tags_norm(tags):
    return [normalizar_html(t) for t in tags]


def _diff(a, b, rotulo, maximo):
    linhas = list(difflib.unified_diff(a, b, "base74", "alvo83", n=0, lineterm=""))
    if not linhas:
        print(f"  [{rotulo}] igual")
        return
    mais = sum(1 for ln in linhas if ln.startswith("+") and not ln.startswith("+++"))
    menos = sum(1 for ln in linhas if ln.startswith("-") and not ln.startswith("---"))
    print(f"  [{rotulo}] -{menos} +{mais} linhas")
    for ln in linhas[2:2 + maximo]:
        print("    " + ln[:220])
    if len(linhas) - 2 > maximo:
        print(f"    ... ({len(linhas) - 2 - maximo} linhas omitidas)")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("urls", nargs="+")
    ap.add_argument("--salvar", help="diretorio para gravar html/texto de cada ambiente")
    ap.add_argument("--max", type=int, default=60, help="maximo de linhas de diff por secao")
    ap.add_argument("--ambientes", default="base74,alvo83")
    ap.add_argument("--espera", type=int, default=1500, help="ms apos o load antes de capturar (cargas ajax)")
    a = ap.parse_args()
    rot_a, rot_b = a.ambientes.split(",")

    from playwright.sync_api import sync_playwright

    with sync_playwright() as p:
        nav = p.chromium.launch()
        pags = {}
        for rot in (rot_a, rot_b):
            ctx = nav.new_context(viewport={"width": 1366, "height": 900})
            pg = ctx.new_page()
            pg.on("dialog", lambda d: d.dismiss())
            _login(pg, f"http://localhost:{AMBIENTES[rot][0]}")
            pags[rot] = pg
        for url in a.urls:
            r = {rot: _coletar(pags[rot], f"http://localhost:{AMBIENTES[rot][0]}", url, a.espera) for rot in (rot_a, rot_b)}
            ra, rb = r[rot_a], r[rot_b]
            na, nb = normalizar_html(ra["html"]), normalizar_html(rb["html"])
            pct = abs(len(na) - len(nb)) * 100 / max(len(na), 1)
            print("=" * 100)
            print(f"{url}\n  status {ra['status']} x {rb['status']} | final {ra['final']} x {rb['final']}"
                  f" | html_norm {len(na)} x {len(nb)} ({pct:.1f}%) | texto {len(ra['texto'])} x {len(rb['texto'])}")
            _diff(_linhas_texto(ra["texto"]), _linhas_texto(rb["texto"]), "texto visivel", a.max)
            _diff(_tags_norm(ra["tags"]), _tags_norm(rb["tags"]), "tags", a.max)
            for rot, rr in ((rot_a, ra), (rot_b, rb)):
                for e in rr["erros"]:
                    print(f"  [console {rot}] {e}")
                for f in rr["falhas"]:
                    print(f"  [http {rot}] {f}")
            so_b = sorted(set(rb["erros"]) - set(ra["erros"]))
            so_bf = sorted(set(re.sub(r":\d+/", "/", x) for x in rb["falhas"]) -
                           set(re.sub(r":\d+/", "/", x) for x in ra["falhas"]))
            if so_b or so_bf:
                print(f"  >>> SO NO {rot_b}: " + " | ".join(so_b + so_bf))
            if a.salvar:
                d = Path(a.salvar)
                d.mkdir(parents=True, exist_ok=True)
                nome = re.sub(r"[^\w.-]+", "_", url.strip("/"))[:120] or "raiz"
                for rot, rr in ((rot_a, ra), (rot_b, rb)):
                    (d / f"{nome}.{rot}.html").write_text(rr["html"], encoding="utf8")
                    (d / f"{nome}.{rot}.txt").write_text(rr["texto"], encoding="utf8")
            sys.stdout.flush()
        nav.close()


if __name__ == "__main__":
    main()
