"""r07_build.py: render the manuscript from its token template and compile it with the journal's template.

Steps: merge the values files (values.json, values3.json, values4.json; a name may live in one file only),
concatenate tex/src/*.tex, substitute every {{name|format}} token (vals.render), insert the CRediT statement
and the ORCID list from inputs/authors.json unchanged, write refs.bib from inputs/references.json, copy the
class file, the style file and the figures into one flat folder (tex/build), and run pdflatex, bibtex and
pdflatex twice. Also writes a skeleton render (every token replaced by the sign § and every URL by ¤) for the
language lint. Imported by r08_check.py; run alone it builds and prints the LaTeX warnings.
"""
import sys, json, re, os, shutil, subprocess, glob
sys.path.insert(0, "/home/claude/mill/phase4/code")
sys.path.insert(0, "/home/claude/mill/code")
from vals import render, fmt_value, TOKEN

P3, P4 = "/home/claude/mill/phase3", "/home/claude/mill/phase4"
SRC, BUILD = f"{P4}/tex/src", f"{P4}/tex/build"
TPL = f"{P4}/template/sn/sn-article-template"


def merged_values():
    files = {"values.json": "/home/claude/mill/outputs/values.json", "values3.json": f"{P3}/outputs/values3.json",
             "values4.json": f"{P4}/outputs/values4.json"}
    out, owner = {}, {}
    for fname, path in files.items():
        for k, v in json.load(open(path)).items():
            if k in out:
                raise SystemExit(f"value name {k} is defined in {owner[k]} and {fname}")
            out[k], owner[k] = v, fname
    return out, owner


def template():
    parts = sorted(glob.glob(f"{SRC}/*.tex"))
    return "\n".join(open(p).read() for p in parts), [os.path.basename(p) for p in parts]


def credit_block(authors):
    lines = []
    for name, roles in authors["credit"]:
        roles_tex = roles.replace("\u2013", "--").replace("&", "\\&")
        lines.append(f"{name}: {roles_tex}.")
    return "\n".join(lines)


def orcid_block(authors):
    out = []
    for a in authors["authors"]:
        who = f"{a['given']} {a['family']}"
        out.append(f"{who}: {a['orcid']}" if a["orcid"] else f"{who}: none")
    return "; ".join(out) + "."


def bibtex(refs):
    prot = ["MTConnect", "MTconnect", "AI/ML", "CNC", "NumPy", "Python", "ROC", "Case Western Reserve University", "CWRU", "Fourier", "Matplotlib", "2D"]
    out = []
    for r in refs["references"]:
        f = {"author": " and ".join(r["author"]), "title": r["title"]}
        for p in prot:
            f["title"] = re.sub(r"(?<![{\w])" + re.escape(p) + r"(?![\w}])", "{" + p + "}", f["title"])
        f["title"] = f["title"].replace("&", "\\&")
        for k in ("journal", "booktitle", "year", "volume", "number", "pages", "doi", "url", "publisher", "howpublished"):
            if r.get(k):
                f[k] = r[k].replace("&", "\\&") if k in ("journal", "booktitle") else r[k]
        if r.get("note_bib"):
            f["note"] = r["note_bib"]
        body = ",\n".join(f"  {k} = {{{v}}}" for k, v in f.items())
        out.append(f"@{r['bibtype']}{{{r['key']},\n{body}\n}}")
    return "\n\n".join(out) + "\n"


def render_all(values):
    tpl, parts = template()
    authors = json.load(open(f"{P4}/inputs/authors.json"))
    missing = []

    def sub(m):
        name, spec = m.group(1), (m.group(2) or "")
        if name not in values:
            missing.append(name)
            return m.group(0)
        out = fmt_value(values[name], spec)
        return re.sub(r"(?<![\w.])-(?=\d)", r"\\textminus{}", out)   # a negative number takes a minus sign, never a hyphen

    tex = TOKEN.sub(sub, tpl)
    tex = tex.replace("%%CREDIT%%", credit_block(authors)).replace("%%ORCID%%", orcid_block(authors))
    skel = TOKEN.sub("§", tpl)
    skel = re.sub(r"\\url\{[^}]*\}", "¤", skel)
    skel = skel.replace("%%CREDIT%%", "¤").replace("%%ORCID%%", "¤")
    return tex, skel, missing, parts


def compile_tex():
    logs = []
    env = dict(os.environ)
    for cmd in (["pdflatex", "-interaction=nonstopmode", "manuscript.tex"], ["bibtex", "manuscript"],
                ["pdflatex", "-interaction=nonstopmode", "manuscript.tex"], ["pdflatex", "-interaction=nonstopmode", "manuscript.tex"]):
        p = subprocess.run(cmd, cwd=BUILD, capture_output=True, text=True, env=env, timeout=300)
        logs.append((" ".join(cmd), p.returncode))
    return logs


def build():
    values, owner = merged_values()
    tex, skel, missing, parts = render_all(values)
    if missing:
        raise SystemExit(f"missing value names in the template: {sorted(set(missing))}")
    if os.path.isdir(BUILD):
        shutil.rmtree(BUILD)
    os.makedirs(BUILD)
    open(f"{BUILD}/manuscript.tex", "w").write(tex)
    open(f"{P4}/tex/manuscript_skeleton.tex", "w").write(skel)
    open(f"{BUILD}/refs.bib", "w").write(bibtex(json.load(open(f"{P4}/inputs/references.json"))))
    shutil.copy(f"{TPL}/sn-jnl.cls", BUILD)
    shutil.copy(f"{P4}/template/journal_files/sn-basic.bst", BUILD)   # the two SORT lines commented out, as the file header instructs for a consecutively numbered list
    for i in range(1, 6):
        shutil.copy(f"{P4}/figures/Fig{i}.eps", BUILD)
    logs = compile_tex()
    return {"parts": parts, "compile": logs, "values": values, "owner": owner, "tex": tex, "skeleton": skel}


if __name__ == "__main__":
    b = build()
    print(b["compile"])
    log = open(f"{BUILD}/manuscript.log", errors="replace").read()
    for pat in (r"^! .*", r"LaTeX Warning: .*", r"Overfull .*", r"Underfull .*"):
        for m in re.finditer(pat, log, flags=re.M):
            print(m.group(0)[:160])
