"""Compara duas rodadas de crawl (base x alvo) e gera relatorio."""
import argparse
import json
import re
from pathlib import Path


def _norm_log(linha: str) -> str:
    """Remove o que muda entre as instalacoes: raiz do codigo e numero de linha."""
    linha = re.sub(r"/var/www/html/accia(-php83)?/", "APP/", linha)
    return re.sub(r"(\.php)(:| on line | \()\d+\)?", lambda m: m.group(1), linha)


def classificar(base: dict, alvo: dict, limiar_pct: float = 2.0) -> dict:
    if base["status"] != alvo["status"]:
        return {"veredito": "divergente", "motivos": [f"status {base['status']} -> {alvo['status']}"]}
    if base["status"] is not None and base["status"] >= 400:
        # a tela ja respondia erro no 7.4 com o mesmo status: preexistente (a mensagem pode mudar de linha)
        return {"veredito": "igual", "motivos": [f"erro preexistente no 7.4 (status {base['status']})"]}
    if alvo["erro_yii"] and not base["erro_yii"]:
        return {"veredito": "divergente", "motivos": ["erro_yii so no alvo"]}
    base_norm = {_norm_log(l) for l in base["log_novos"]}
    novos_log = [l for l in alvo["log_novos"] if _norm_log(l) not in base_norm]
    if novos_log:
        return {"veredito": "divergente", "motivos": ["log novo: " + novos_log[0]]}
    novos_js = [e for e in alvo["console_erros"] if e not in base["console_erros"]]
    if novos_js:
        return {"veredito": "divergente", "motivos": ["console novo: " + novos_js[0]]}
    if base["erro_yii"] and alvo["erro_yii"]:
        return {"veredito": "igual", "motivos": ["erro preexistente no 7.4"]}
    if base["html_norm_sha"] == alvo["html_norm_sha"]:
        return {"veredito": "igual", "motivos": []}
    maior = max(base["html_norm_len"], alvo["html_norm_len"], 1)
    dif = abs(base["html_norm_len"] - alvo["html_norm_len"]) * 100.0 / maior
    if dif <= limiar_pct:
        return {"veredito": "igual", "motivos": [f"ruido ({dif:.2f}% de tamanho)"]}
    return {"veredito": "divergente", "motivos": [f"conteudo ({dif:.2f}% de tamanho)"]}


def gerar_relatorio(crawl_base: Path, crawl_alvo: Path, saida: Path) -> dict:
    base = {r["url"]: r for r in json.loads(Path(crawl_base).read_text(encoding="utf8"))["registros"]}
    alvo = {r["url"]: r for r in json.loads(Path(crawl_alvo).read_text(encoding="utf8"))["registros"]}
    linhas, resumo = [], {"igual": 0, "divergente": 0, "so_base": 0, "so_alvo": 0}
    for url in sorted(set(base) | set(alvo)):
        if url not in alvo:
            resumo["so_base"] += 1
            linhas.append({"url": url, "veredito": "so_base", "motivos": []})
            continue
        if url not in base:
            resumo["so_alvo"] += 1
            linhas.append({"url": url, "veredito": "so_alvo", "motivos": []})
            continue
        c = classificar(base[url], alvo[url])
        resumo[c["veredito"]] += 1
        linhas.append({"url": url, "status_base": base[url]["status"], "status_alvo": alvo[url]["status"], **c})
    saida = Path(saida)
    saida.with_suffix(".json").write_text(json.dumps({"resumo": resumo, "linhas": linhas}, ensure_ascii=False, indent=1), encoding="utf8")
    md = [f"# Comparacao {Path(crawl_base).stem} x {Path(crawl_alvo).stem}", "",
          "| Resultado | Qtde |", "|---|---|"] + [f"| {k} | {v} |" for k, v in resumo.items()] + [
          "", "## Divergencias", "", "| URL | Status base | Status alvo | Motivo |", "|---|---|---|---|"]
    for l in linhas:
        if l["veredito"] != "igual":
            md.append(f"| `{l['url']}` | {l.get('status_base','-')} | {l.get('status_alvo','-')} | {l['veredito']}: {'; '.join(l['motivos'])[:200]} |")
    saida.with_suffix(".md").write_text("\n".join(md) + "\n", encoding="utf8")
    return resumo


if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--base", required=True)
    ap.add_argument("--alvo", required=True)
    ap.add_argument("--saida", required=True)
    print(gerar_relatorio(Path(ap.parse_args().base), Path(ap.parse_args().alvo), Path(ap.parse_args().saida)))
