# -*- coding: utf-8 -*-
"""
epd_lookup — o conector da ponte EPD→BIM (v1, fonte: ÖKOBAUDAT API aberta).

O que faz:
  1. pesquisa processos por nome na API pública (sem autenticação);
  2. saca o detalhe (ILCD JSON) do escolhido;
  3. imprime os indicadores (GWP etc.) por módulo de vida (A1…D);
  4. emite um bloco PSET pronto a injetar no IFC (Pset_EnvironmentalImpactValues,
     marcado GENÉRICO com UUID/versão para rastreabilidade) + guarda o JSON.

API (descoberta por interceção da pesquisa oficial, 2026-09-03):
  lista:   GET {BASE}/processes?format=json&search=true&name=<termo>&…
  detalhe: GET {BASE}/processes/<uuid>?format=json&lang=en&version=<v>

Uso:
  py epd_lookup.py "C30"                 # pesquisa + pset do melhor resultado
  py epd_lookup.py "rock wool" -n 8      # lista 8 candidatos
  py epd_lookup.py --uuid <uuid> --version 00.01.000
Saída: consola + epd/lookup_*.json (cache permanente para auditoria).
"""
import argparse
import json
import os
import re
import ssl
import sys
import time
import urllib.parse
import urllib.request

HERE = os.path.dirname(os.path.abspath(__file__))
CACHE = os.path.join(HERE, "epd")
os.makedirs(CACHE, exist_ok=True)
BASE = "https://oekobaudat.de/OEKOBAU.DAT/resource/datastocks/cc64f7e1-14d8-4a57-b11b-2cf03d200c82/processes"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0"
CTX = ssl.create_default_context(); CTX.check_hostname = False; CTX.verify_mode = ssl.CERT_NONE


def get(url, tries=3):
    for i in range(tries):
        try:
            req = urllib.request.Request(url, headers={"User-Agent": UA, "Accept": "application/json"})
            with urllib.request.urlopen(req, timeout=45, context=CTX) as r:
                return json.loads(r.read().decode("utf-8"))
        except Exception as e:  # noqa: BLE001
            if i == tries - 1:
                raise
            time.sleep(2 * (i + 1))
    return None


def search(term, n):
    q = urllib.parse.quote(term)
    url = f"{BASE}?format=json&search=true&startIndex=0&pageSize={n}&sortOrder=true&sortBy=name&lang=en&name={q}"
    d = get(url)
    out = []
    for p in d.get("data", []):
        out.append({"nome": p.get("name"), "uuid": p.get("uuid"), "version": p.get("version"),
                    "classe": (p.get("classifications") or [{}])[0].get("name") if p.get("classifications") else None})
    return d.get("totalCount", 0), out


def en_label(shortdesc):
    if isinstance(shortdesc, str):
        return shortdesc
    for part in shortdesc or []:
        if part.get("lang") == "en":
            return part.get("value")
    return (shortdesc or [{}])[0].get("value", "?") if shortdesc else "?"


def detail(uuid, version):
    url = f"{BASE}/{uuid}?format=json&lang=en&version={version}"
    d = get(url)
    pi = (d.get("processInformation") or {}).get("dataSetInformation") or {}
    name = en_label((pi.get("name") or {}).get("baseName"))
    # classificação: classificationInformation.classification[] → class[] (value/level)
    classes = []
    try:
        for c in pi["classificationInformation"]["classification"]:
            path = " > ".join(x.get("value", "") for x in sorted(c.get("class", []), key=lambda y: y.get("level", 0)))
            if path:
                classes.append((c.get("name", ""), path))
    except Exception:  # noqa: BLE001
        pass
    # objeto de referência: exchange[0] (meanAmount + nome do flow)
    refobj = ""
    try:
        ex = d["exchanges"]["exchange"][0]
        flow = en_label((ex.get("referenceToFlowDataSet") or {}).get("shortDescription")) or ""
        refobj = f"{ex.get('meanAmount', 1):g} {flow}".strip()
    except Exception:  # noqa: BLE001
        pass
    # indicadores (unidade vem DENTRO de anies em referenceToUnitGroupDataSet)
    indicators = []
    for r in (d.get("LCIAResults") or {}).get("LCIAResult") or []:
        labels = " / ".join(str(p.get("value", "")) for p in (r.get("referenceToLCIAMethodDataSet") or {}).get("shortDescription") or [])
        method = en_label((r.get("referenceToLCIAMethodDataSet") or {}).get("shortDescription")) or labels
        is_gwp = bool(re.search(r"GWP|Climate change|Treibhaus", labels, re.I))
        mods, unit = {}, ""
        for a in (r.get("other") or {}).get("anies") or []:
            if a.get("name") == "referenceToUnitGroupDataSet":
                unit = en_label((a.get("value") or {}).get("shortDescription"))
            elif a.get("module") and a.get("value") not in (None, ""):
                key = a["module"] + (f" [{a['scenario']}]" if a.get("scenario") else "")
                try:
                    mods[key] = float(a["value"])
                except ValueError:
                    pass
        if mods:
            indicators.append({"metodo": method, "unidade": unit, "modulos": mods, "gwp": is_gwp})
    return {"nome": name, "uuid": uuid, "version": version, "classes": classes,
            "referencia": refobj, "indicadores": indicators,
            "url": "https://www.oekobaudat.de/en/database/search.html (datastock geral)"}


def gwp_a123(rec):
    """GWP-total A1–A3, somando A1+A2+A3 (tolerante a sufixos de cenário: 'A1 [Standard scenario]')."""
    gwp = next((i for i in rec.get("indicadores", []) if i.get("gwp")), None)
    if not gwp:
        return None, None
    m = gwp["modulos"]
    if "A1-A3" in m:
        v = m["A1-A3"]
    else:
        v = sum(val for key, val in m.items()
                if re.sub(r"\s*\[.*?\]\s*", "", key) in ("A1", "A2", "A3"))
    return round(v, 4), gwp.get("unidade")


def pset_block(rec):
    lines = [f"; ── Pset_EnvironmentalImpactValues [GENÉRICO — ÖKOBAUDAT {rec['uuid']} v{rec['version']}] ──",
             f"; dataset: {rec['nome']}  ({rec['referencia']})"]
    for src, path in rec["classes"]:
        lines.append(f"; classe {src}: {path}")
    keep = ("Global Warming", "Primary energy", "ADP", "Acidification", "Eutrophication",
            "Ozone", "Photochem", "Water", "Abiotic")
    for ind in rec["indicadores"]:
        if not any(k.lower() in ind["metodo"].lower() for k in keep):
            continue
        tag = re.sub(r"[^A-Za-z0-9]+", "_", ind["metodo"]).strip("_")[:48]
        unit = re.sub(r"[^A-Za-z0-9/^*-]+", "_", ind.get("unidade") or "").strip("_")
        for mod, val in ind["modulos"].items():
            m = re.sub(r"[^A-Za-z0-9]+", "_", mod.replace(" [", "_").replace("]", "")).strip("_")
            lines.append(f"{tag}__{m} = {val:g}" + (f" ; {unit}" if unit else ""))
    return "\n".join(lines)


def main():
    ap = argparse.ArgumentParser(description="Conector ÖKOBAUDAT → pset IFC")
    ap.add_argument("termo", nargs="?", help="termo de pesquisa (nome do processo)")
    ap.add_argument("-n", type=int, default=5, help="n.º de candidatos a listar (default 5)")
    ap.add_argument("--uuid"); ap.add_argument("--version")
    ap.add_argument("--index", type=int, default=1, help="usar o N-ésimo resultado da pesquisa (default 1)")
    a = ap.parse_args()
    if not a.uuid and not a.termo:
        ap.error("indica um termo de pesquisa ou --uuid")
    if a.termo and not a.uuid:
        total, hits = search(a.termo, max(a.n, a.index))
        print(f"≈ {total} processos; top {len(hits)}:")
        for i, h in enumerate(hits, 1):
            print(f"  [{i}] {str(h['nome'])[:74]:74} v{h['version']}")
        if not hits:
            sys.exit("sem resultados")
        pick = hits[min(a.index, len(hits)) - 1]
        a.uuid, a.version = pick["uuid"], pick["version"]
    rec = detail(a.uuid, a.version)
    out_json = os.path.join(CACHE, f"lookup_{a.uuid[:8]}.json")
    with open(out_json, "w", encoding="utf-8") as f:
        json.dump(rec, f, ensure_ascii=False, indent=1)
    print(f"\n── {rec['nome']} · ref: {rec['referencia']}")
    for src, path in rec["classes"]:
        print(f"   classe {src}: {path}")
    for ind in rec["indicadores"]:
        mods = "  ".join(f"{m}={v:g}" for m, v in ind["modulos"].items())
        print(f"   {ind['metodo'][:46]:46} [{ind['unidade']:12}] {mods[:85]}")
    print("\n" + pset_block(rec) + f"\n\nJSON completo: {out_json}")


if __name__ == "__main__":
    main()
