#!/usr/bin/env python3
"""Prévia PNG direta do modelo, não captura de navegador. Requer ImageMagick opcional."""
import argparse
import importlib.util
import json
import math
from pathlib import Path
import shutil
import subprocess
import sys
import textwrap
import xml.etree.ElementTree as ET
sys.dont_write_bytecode = True
module = Path(__file__).with_name("gerar_mapa.py")
spec = importlib.util.spec_from_file_location("mapa_ipo", module)
g = importlib.util.module_from_spec(spec)
spec.loader.exec_module(g)

def main():
    p = argparse.ArgumentParser(description=__doc__)
    p.add_argument("modelo", type=Path)
    p.add_argument("png", type=Path)
    p.add_argument("--fonte", default="Arial")
    p.add_argument("--substituir", action="store_true")
    args = p.parse_args()
    try:
        m = g.validate(json.loads(args.modelo.read_text()))
        view = m["vistas"][0]
        width, height = g.geometry(view)
        height += 215
        g.require(args.png.suffix.lower() == ".png", "Saída deve ser .png.")
        mvg = args.png.with_suffix(".mvg")
        g.require(args.png.parent.is_dir(), "Pasta de destino precisa existir.")
        g.require(args.substituir or not (args.png.exists() or mvg.exists()), "Prévia já existe.")
        executable = shutil.which("magick")
        g.require(executable is not None, "ImageMagick indisponível; HTML e Markdown continuam utilizáveis.")
        drawing = []
        def safe(value):
            return str(value).replace("\\", "\\\\").replace("'", "\\'").replace("\n", " ")
        def text(x, y, value, size=15, color="#465970", weight=400, anchor="start"):
            drawing.extend(["push graphic-context", "stroke none", f"fill '{color}'", f"font-size {size}", f"font-weight {weight}", f"text-anchor {anchor}", f"text {x},{y} '{safe(value)}'", "pop graphic-context"])
        def line(points, color="#71859b", dashed=False):
            drawing.extend(["push graphic-context", f"stroke '{color}'", "stroke-width 1.7", "fill none"])
            if dashed: drawing.append("stroke-dasharray 5 5")
            drawing.extend(["polyline " + " ".join(f"{x},{y}" for x,y in points), "pop graphic-context"])
            (px,py),(x,y)=points[-2:]
            angle=math.atan2(y-py,x-px)
            a=(x-10*math.cos(angle-.4), y-10*math.sin(angle-.4))
            b=(x-10*math.cos(angle+.4), y-10*math.sin(angle+.4))
            drawing.extend(["push graphic-context",f"stroke '{color}'","stroke-width 1.7","fill none","polyline "+ " ".join(f"{xx},{yy}" for xx,yy in [a,(x,y),b]),"pop graphic-context"])
        def rounded(x,y,w,h,fill="#f8fafc",stroke="#ced8e3"):
            drawing.extend(["push graphic-context",f"fill '{fill}'",f"stroke '{stroke}'","stroke-width 1",f"roundrectangle {x},{y} {x+w},{y+h} 10,10","pop graphic-context"])
        def icon(icon_id,x,y,sz=32):
            shapes=g.icon_shapes(icon_id)
            root=ET.fromstring('<svg>'+shapes+'</svg>')
            drawing.extend(["push graphic-context",f"translate {x},{y}",f"scale {sz/24},{sz/24}"])
            def element(el):
                tag=el.tag.split("}")[-1];a=el.attrib
                if tag=="g":
                    drawing.append("push graphic-context")
                    if a.get("fill")=="none":drawing.append("fill none")
                    else:drawing.append("fill '#174d88'")
                    if a.get("stroke")=="none":drawing.append("stroke none")
                    elif a.get("stroke"):drawing.extend(["stroke '#174d88'","stroke-width 1.8","stroke-linecap round","stroke-linejoin round"])
                if tag=="path": drawing.append("path '"+safe(a["d"])+"'")
                elif tag=="rect":drawing.append(f"roundrectangle {a['x']},{a['y']} {float(a['x'])+float(a['width'])},{float(a['y'])+float(a['height'])} {a.get('rx',0)},{a.get('ry',a.get('rx',0))}")
                elif tag=="circle":drawing.append(f"circle {a['cx']},{a['cy']} {float(a['cx'])+float(a['r'])},{a['cy']}")
                elif tag=="ellipse":drawing.append(f"ellipse {a['cx']},{a['cy']} {a['rx']},{a['ry']} 0,360")
                elif tag=="line":drawing.append(f"line {a['x1']},{a['y1']} {a['x2']},{a['y2']}")
                elif tag in {"polygon","polyline"}:drawing.append(tag+" "+a["points"])
                for child in el:element(child)
                if tag=="g":drawing.append("pop graphic-context")
            element(root)
            drawing.append("pop graphic-context")
        drawing.extend(["push graphic-context",f"viewbox 0 0 {width} {height}","fill '#fbfcfe'",f"rectangle 0,0 {width},{height}",f"font '{safe(args.fonte)}'"])
        text(40,35,"COUTO-IAS · PRÉVIA DO MODELO · MAPA DO AGENTE",12)
        text(40,78,m["titulo"],34,"#172538",600)
        text(40,111,m["pergunta"],19)
        drawing.extend(["push graphic-context","translate 0,140"])
        nodes={n["id"]:n for n in m["nos"]}
        rels={r["id"]:r for r in m["relacoes"]}
        for label in view.get("rotulos",[]):text(label["x"],label["y"],label["texto"],15,"#172538",600)
        for edge in view["arestas"]:
            r=rels[edge["relacao"]]
            line(edge["pontos"],dashed=r["natureza"] in {"dependencia","controle","conhecimento"})
            text(*edge["rotulo"],r["verbo"]+" "+r["objeto"],13,anchor="middle")
        for nid,(x,y) in view["posicoes"].items():
            n=nodes[nid];w,h=g.size(view,nid)
            central=nid in m.get("ipo",{}).get("transformacoes",[])
            rounded(x,y,w,h,fill="#edf3fa" if central else "#fbfcfe",stroke="#9bb6d0" if central else "#dde4ec")
            icon(n.get("icone","v-sistema"),x+16,y+18,40 if central else 30)
            tx=x+68;ty=y+23
            text(tx,ty,n["tipo"].upper(),10)
            title_lines=textwrap.wrap(n["nome"],22 if central else 28)
            for i,part in enumerate(title_lines):text(tx,ty+28+i*25,part,24 if central else 20,"#172538",600)
            current=ty+36+len(title_lines)*25
            for part in textwrap.wrap(n.get("linha",n["papel"]),37):
                text(tx,current,part,13);current+=17
            if central:
                for part in textwrap.wrap(n["papel"],37):
                    text(x+24,y+165+17*textwrap.wrap(n["papel"],37).index(part),part,15)
            text(tx,y+h-12,g.PREPARO.get(n.get("preparo"),"") + " · " + n["afirmacao"],11,"#61428b",600)
        drawing.append("pop graphic-context")
        text(40,height-39,m["condicao"] + " · Confira requisitos e limites no documento-mapa.",13)
        text(40,height-17,"Prévia estática direta do modelo JSON. Não é captura de HTML nem prova de interação.",12)
        drawing.append("pop graphic-context")
        mvg.write_text("\n".join(drawing))
        result=subprocess.run([executable,"-size",f"{int(width)}x{int(height)}","-background","#fbfcfe","mvg:"+str(mvg),str(args.png)],capture_output=True,text=True)
        g.require(result.returncode==0,"Falha de renderização: "+result.stderr[:800])
        print(json.dumps({"png":str(args.png),"modelo":str(args.modelo),"tipo":"renderização direta do modelo; não captura de navegador"},ensure_ascii=False))
    except (ValueError, OSError, KeyError, TypeError) as exc:
        print("Erro: "+str(exc),file=sys.stderr)
        return 2
    return 0
if __name__=="__main__":
    sys.exit(main())

