#!/usr/bin/env python3
"""
Field & Table-of-Contents Showcase — generates fields.docx exercising the docx
`field` and `toc` element surface (schemas/help/docx/field.json + toc.json).

A Word FIELD is a complex fldChar run (begin / instrText / separate / result /
end), addressed as a whole at /field[N]. This builds a small report:

  Title + TOC   — a table of contents over heading levels 1-3
  Sections      — Heading1/Heading2 paragraphs the TOC references
  DATE / TIME   — fields with picture (\@) switches
  REF           — a cross-reference to a bookmark, \h = clickable
  IF            — a conditional field (expression + true/false text)
  HYPERLINK     — via a raw instruction (no typed URL shortcut)
  TITLE         — a document-property field
  Locked PAGE   — fldLock=true, Word won't recalc on F9
  Footer        — composite "Page X of Y" (PAGE + literal + NUMPAGES)

IMPORTANT — fields carry only their CACHED result until Word updates them.
officecli writes the field CODES correctly; Word computes the
live values on open, or when you press F9 / Update Field.

Like examples/word/document-formatting.py, this drives the officecli Python SDK
(`pip install officecli-sdk`): one resident, writes shipped over the pipe.

Usage:
  python3 fields.py
"""

import os
import sys
import subprocess

try:
    import officecli  # pip install officecli-sdk
except ImportError:
    sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                    "..", "..", "sdk", "python"))
    import officecli

FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fields.docx")


def para(text, **props):
    return {"command": "add", "parent": "/body", "type": "paragraph",
            "props": {"text": text, **props}}


def field(parent="/body", **props):
    return {"command": "add", "parent": parent, "type": "field", "props": props}


def toc(**props):
    return {"command": "add", "parent": "/body", "type": "toc", "props": props}


print("\n==========================================")
print(f"Generating field & TOC showcase: {FILE}")
print("==========================================")

with officecli.create(FILE, "--force") as doc:

    # ----------------------------------------------------------------------
    # Heading styles + updateFields — REQUIRED for a populated TOC.
    # A TOC field gathers paragraphs by OUTLINE LEVEL (from the paragraph
    # style), so the built-in heading styles must exist with an explicit
    # outlineLvl (0 = Heading 1). updateFields=true then makes Word recompute
    # every field on open, so the TOC fills in with real page numbers.
    # ----------------------------------------------------------------------
    print("\n--- Heading styles + updateFields ---")
    doc.batch([
        {"command": "add", "parent": "/styles", "type": "style",
         "props": {"id": "Heading1", "name": "heading 1", "type": "paragraph",
                   "outlineLvl": "0", "bold": "true", "size": "16",
                   "color": "1F3864"}},
        {"command": "add", "parent": "/styles", "type": "style",
         "props": {"id": "Heading2", "name": "heading 2", "type": "paragraph",
                   "outlineLvl": "1", "bold": "true", "size": "13",
                   "color": "2E5496"}},
    ])
    doc.send({"command": "set", "path": "/", "props": {"updateFields": "true"}})

    # Title + table of contents (references the headings added below)
    # ----------------------------------------------------------------------
    print("\n--- Title + TOC ---")
    doc.batch([
        para("Field & Table-of-Contents Showcase", style="Title"),
        para("Every field below shows its cached result until Word updates it "
             "(F9); this doc sets updateFields so Word fills the TOC on open.",
             italic="true", color="666666"),
        # TOC field over heading levels 1-3, clickable, with page numbers.
        toc(title="Contents", levels="1-3", hyperlinks="true",
            pageNumbers="true"),
    ])

    # ----------------------------------------------------------------------
    # Section 1 — Introduction (bookmarked for the REF cross-reference)
    # ----------------------------------------------------------------------
    print("--- Section 1: Introduction (+ bookmark) ---")
    doc.batch([
        para("1. Introduction", style="Heading1"),
        {"command": "add", "parent": "/body", "type": "bookmark",
         "props": {"name": "IntroSection", "text": "Introduction"}},
        para("This report is generated by officecli. It demonstrates the full "
             "docx field surface: page numbering, dates, cross-references, "
             "conditional (IF) fields, hyperlinks, and an automatic table of "
             "contents."),
        para("1.1 Scope", style="Heading2"),
        para("Fields are computed values Word maintains for you. officecli "
             "writes the field code; the value you see is a cached result "
             "until the next update."),
    ])

    # ----------------------------------------------------------------------
    # Section 2 — DATE & TIME fields (picture switches)
    # ----------------------------------------------------------------------
    print("--- Section 2: DATE & TIME ---")
    doc.batch([
        para("2. Date & Time Fields", style="Heading1"),
        para("Report date (DATE, formatted yyyy-MM-dd):"),
        # `format` is a bare picture string; handler wraps it into \@ "...".
        field(fieldType="date", format="yyyy-MM-dd"),
        para("Generated at (TIME, formatted HH:mm):"),
        field(fieldType="time", format="HH:mm"),
    ])

    # ----------------------------------------------------------------------
    # Section 3 — REF cross-reference to the IntroSection bookmark
    # ----------------------------------------------------------------------
    print("--- Section 3: REF cross-reference ---")
    doc.batch([
        para("3. Cross-References", style="Heading1"),
        para("See the section titled:"),
        # \h switch makes the reference a clickable hyperlink to the target.
        field(fieldType="ref", bookmarkName="IntroSection", hyperlink="true"),
    ])

    # ----------------------------------------------------------------------
    # Section 4 — IF conditional field
    # ----------------------------------------------------------------------
    print("--- Section 4: IF conditional ---")
    doc.batch([
        para("4. Conditional Fields", style="Heading1"),
        para("An IF field picks one of two texts from a logical expression:"),
        # expression + trueText/falseText fold into the instruction.
        field(fieldType="if", expression="1 = 1",
              trueText="Condition is TRUE", falseText="Condition is FALSE"),
    ])

    # ----------------------------------------------------------------------
    # Section 5 — HYPERLINK (raw instruction) + TITLE (doc property)
    # ----------------------------------------------------------------------
    print("--- Section 5: HYPERLINK & TITLE ---")
    doc.batch([
        para("5. Hyperlink & Property Fields", style="Heading1"),
        para("A HYPERLINK field (raw instruction — no typed shortcut for the "
             "URL form):"),
        # `instruction` bypasses the typed helpers for arbitrary field codes.
        field(instruction=' HYPERLINK "https://example.com" \\o "Visit example.com" '),
        para("The document title, pulled from file metadata (TITLE field):"),
        field(fieldType="title"),
    ])

    # ----------------------------------------------------------------------
    # Section 6 — a locked PAGE field (Word won't recalc it on F9)
    # ----------------------------------------------------------------------
    print("--- Section 6: locked PAGE field ---")
    doc.batch([
        para("6. Locked Fields", style="Heading1"),
        para("A locked PAGE field keeps its cached result even on Update "
             "Field:"),
        # fldLock=true persists in OOXML and is surfaced on get (fldLock=true,
        # only when the field is locked).
        field(fieldType="page", fldLock="true"),
    ])

    # ----------------------------------------------------------------------
    # Footer — composite "Page X of Y" built in steps on /footer[1]/p[1]
    # ----------------------------------------------------------------------
    print("--- Footer: 'Page X of Y' ---")
    doc.batch([
        {"command": "add", "parent": "/", "type": "footer",
         "props": {"text": "Page ", "align": "center"}},
        field(parent="/footer[1]/p[1]", fieldType="page"),
        {"command": "add", "parent": "/footer[1]/p[1]", "type": "run",
         "props": {"text": " of "}},
        field(parent="/footer[1]/p[1]", fieldType="numpages"),
    ])

    # ----------------------------------------------------------------------
    # Set after create — retarget the DATE field's picture switch.
    # TOC is /field[1], so the DATE field is /field[2].
    # ----------------------------------------------------------------------
    print("--- Set: retarget DATE format ---")
    doc.send({"command": "set", "path": "/field[2]",
              "props": {"format": "dddd, MMMM d, yyyy"}})

    doc.send({"command": "save"})

    # ----------------------------------------------------------------------
    # Get round-trip: confirm field codes and TOC props read back
    # ----------------------------------------------------------------------
    print("\n--- Round-trip readback ---")
    for path in ["/field[2]", "/field[4]", "/field[5]", "/toc[1]"]:
        node = doc.send({"command": "get", "path": path})
        res = node.get("data", {}).get("results", [{}])[0]
        fmt = res.get("format", {})
        instr = fmt.get("instruction", "")
        extra = ""
        if res.get("type") == "toc":
            extra = (f" levels={fmt.get('levels')} "
                     f"hyperlinks={fmt.get('hyperlinks')} "
                     f"pageNumbers={fmt.get('pageNumbers')}")
        print(f"  {path}: {fmt.get('fieldType', res.get('type'))} "
              f"instruction={instr!r}{extra}")

print("\n--- Validate (fresh process, from disk) ---")
r = subprocess.run(["officecli", "validate", FILE], capture_output=True, text=True)
print(" ", (r.stdout or r.stderr).strip().split("\n")[0])

print(f"\nCreated: {FILE}")
