#!/usr/bin/env python3
"""
Build the neutral slohmaier Word document template.

Output: templates-word/slohmaier-doc-template.docx

The template is ISO 9001 / ASPICE-friendly:
- Cover page with project, doc-ID, version, classification
- Document Control (Approvals, Revision History, Distribution)
- Inhaltsverzeichnis / Abbildungsverzeichnis / Tabellenverzeichnis (auto-fields)
- Custom styles: Heading 1-4, Body, Caption, Code, Note, Warning, Requirement
- Header/Footer with project + doc-ID + classification + page

Run:  python3 tools/build_word_template.py
"""
from __future__ import annotations

import sys
from pathlib import Path

from docx import Document
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.table import WD_ALIGN_VERTICAL, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Pt, RGBColor


# ---------------------------------------------------------------------------
# OXML helpers
# ---------------------------------------------------------------------------

def _qn_set(elem, attr, value):
    elem.set(qn(attr), value)


def add_field(paragraph, instr_text, default_text="<Update field with F9>"):
    """Insert a Word field (e.g. TOC, PAGE) into a paragraph."""
    run = paragraph.add_run()
    r_elem = run._r

    fld_begin = OxmlElement("w:fldChar")
    _qn_set(fld_begin, "w:fldCharType", "begin")
    r_elem.append(fld_begin)

    instr = OxmlElement("w:instrText")
    _qn_set(instr, "xml:space", "preserve")
    instr.text = instr_text
    r_elem.append(instr)

    fld_sep = OxmlElement("w:fldChar")
    _qn_set(fld_sep, "w:fldCharType", "separate")
    r_elem.append(fld_sep)

    default_t = OxmlElement("w:t")
    default_t.text = default_text
    r_elem.append(default_t)

    fld_end = OxmlElement("w:fldChar")
    _qn_set(fld_end, "w:fldCharType", "end")
    r_elem.append(fld_end)


def set_cell_shading(cell, fill_hex):
    tc_pr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement("w:shd")
    _qn_set(shd, "w:val", "clear")
    _qn_set(shd, "w:color", "auto")
    _qn_set(shd, "w:fill", fill_hex)
    tc_pr.append(shd)


def set_cell_borders(cell, color="808080", size=4):
    tc_pr = cell._tc.get_or_add_tcPr()
    tc_borders = OxmlElement("w:tcBorders")
    for edge in ("top", "left", "bottom", "right"):
        b = OxmlElement(f"w:{edge}")
        _qn_set(b, "w:val", "single")
        _qn_set(b, "w:sz", str(size))
        _qn_set(b, "w:space", "0")
        _qn_set(b, "w:color", color)
        tc_borders.append(b)
    tc_pr.append(tc_borders)


def set_paragraph_border(paragraph, color="808080", size=12, side="left"):
    p_pr = paragraph._p.get_or_add_pPr()
    p_bdr = OxmlElement("w:pBdr")
    b = OxmlElement(f"w:{side}")
    _qn_set(b, "w:val", "single")
    _qn_set(b, "w:sz", str(size))
    _qn_set(b, "w:space", "8")
    _qn_set(b, "w:color", color)
    p_bdr.append(b)
    p_pr.append(p_bdr)


def set_paragraph_shading(paragraph, fill_hex):
    p_pr = paragraph._p.get_or_add_pPr()
    shd = OxmlElement("w:shd")
    _qn_set(shd, "w:val", "clear")
    _qn_set(shd, "w:color", "auto")
    _qn_set(shd, "w:fill", fill_hex)
    p_pr.append(shd)


# ---------------------------------------------------------------------------
# Styles
# ---------------------------------------------------------------------------

NEUTRAL_GREY = "595959"
SOFT_GREY = "808080"
LIGHT_GREY = "F2F2F2"
ACCENT_DARK = "1F3864"
NOTE_BG = "E7F0FA"
NOTE_BORDER = "2E74B5"
WARN_BG = "FFF4E5"
WARN_BORDER = "C55A11"
REQ_BG = "EAEAEA"
REQ_BORDER = "404040"


def configure_styles(doc):
    styles = doc.styles

    # --- Normal / Body Text ---
    normal = styles["Normal"]
    normal.font.name = "Calibri"
    normal.font.size = Pt(11)
    normal.font.color.rgb = RGBColor(0x20, 0x20, 0x20)
    normal.paragraph_format.space_after = Pt(6)
    normal.paragraph_format.line_spacing = 1.15

    # --- Headings ---
    heading_specs = [
        ("Heading 1", 18, True, RGBColor(0x1F, 0x38, 0x64), 18, 6),
        ("Heading 2", 14, True, RGBColor(0x1F, 0x38, 0x64), 12, 6),
        ("Heading 3", 12, True, RGBColor(0x40, 0x40, 0x40), 8, 4),
        ("Heading 4", 11, True, RGBColor(0x40, 0x40, 0x40), 6, 4),
    ]
    for name, size, bold, color, before, after in heading_specs:
        s = styles[name]
        s.font.name = "Calibri"
        s.font.size = Pt(size)
        s.font.bold = bold
        s.font.color.rgb = color
        s.paragraph_format.space_before = Pt(before)
        s.paragraph_format.space_after = Pt(after)
        s.paragraph_format.keep_with_next = True

    # --- Title ---
    title = styles["Title"]
    title.font.name = "Calibri"
    title.font.size = Pt(36)
    title.font.bold = True
    title.font.color.rgb = RGBColor(0x1F, 0x38, 0x64)

    # --- Subtitle ---
    subtitle = styles["Subtitle"]
    subtitle.font.name = "Calibri"
    subtitle.font.size = Pt(16)
    subtitle.font.color.rgb = RGBColor(0x59, 0x59, 0x59)

    # --- Caption (figure/table caption) ---
    caption = styles["Caption"]
    caption.font.name = "Calibri"
    caption.font.size = Pt(10)
    caption.font.italic = True
    caption.font.color.rgb = RGBColor(0x59, 0x59, 0x59)

    # --- Code (custom paragraph style) ---
    if "Code" not in [s.name for s in styles]:
        code = styles.add_style("Code", WD_STYLE_TYPE.PARAGRAPH)
        code.base_style = styles["Normal"]
        code.font.name = "Consolas"
        code.font.size = Pt(10)
        code.paragraph_format.left_indent = Cm(0.5)
        code.paragraph_format.space_before = Pt(4)
        code.paragraph_format.space_after = Pt(4)

    # --- Note (custom paragraph style) ---
    if "Note" not in [s.name for s in styles]:
        note = styles.add_style("Note", WD_STYLE_TYPE.PARAGRAPH)
        note.base_style = styles["Normal"]
        note.font.name = "Calibri"
        note.font.size = Pt(10)
        note.paragraph_format.left_indent = Cm(0.4)
        note.paragraph_format.space_before = Pt(6)
        note.paragraph_format.space_after = Pt(6)

    # --- Warning ---
    if "Warning" not in [s.name for s in styles]:
        warn = styles.add_style("Warning", WD_STYLE_TYPE.PARAGRAPH)
        warn.base_style = styles["Normal"]
        warn.font.name = "Calibri"
        warn.font.size = Pt(10)
        warn.font.bold = True
        warn.paragraph_format.left_indent = Cm(0.4)

    # --- Requirement Box ---
    if "Requirement" not in [s.name for s in styles]:
        req = styles.add_style("Requirement", WD_STYLE_TYPE.PARAGRAPH)
        req.base_style = styles["Normal"]
        req.font.name = "Calibri"
        req.font.size = Pt(11)
        req.paragraph_format.left_indent = Cm(0.4)
        req.paragraph_format.space_before = Pt(6)
        req.paragraph_format.space_after = Pt(6)


# ---------------------------------------------------------------------------
# Page setup, header/footer
# ---------------------------------------------------------------------------

def setup_page(doc):
    for section in doc.sections:
        section.top_margin = Cm(2.5)
        section.bottom_margin = Cm(2.5)
        section.left_margin = Cm(2.5)
        section.right_margin = Cm(2.5)
        section.header_distance = Cm(1.25)
        section.footer_distance = Cm(1.25)


def build_header_footer(doc, doc_id_placeholder="<DOC-ID>", classification="CONFIDENTIAL"):
    section = doc.sections[0]
    section.different_first_page_header_footer = True

    # --- Default header (skipped on cover page) ---
    header = section.header
    header_para = header.paragraphs[0]
    header_para.alignment = WD_ALIGN_PARAGRAPH.LEFT
    tabs = header_para.paragraph_format.tab_stops
    tabs.add_tab_stop(Cm(8), WD_ALIGN_PARAGRAPH.CENTER)
    tabs.add_tab_stop(Cm(16), WD_ALIGN_PARAGRAPH.RIGHT)
    r1 = header_para.add_run("<PROJECT>")
    r1.font.size = Pt(9)
    r1.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    header_para.add_run("\t")
    r2 = header_para.add_run("<DOCUMENT-TITLE>")
    r2.font.size = Pt(9)
    r2.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    r2.bold = True
    header_para.add_run("\t")
    r3 = header_para.add_run(doc_id_placeholder)
    r3.font.size = Pt(9)
    r3.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    if LOGO_PATH.exists():
        logo_para = header.add_paragraph()
        logo_para.alignment = WD_ALIGN_PARAGRAPH.RIGHT
        logo_run = logo_para.add_run()
        logo_run.add_picture(str(LOGO_PATH), width=Cm(2.8))

    # --- Default footer (skipped on cover page) ---
    footer = section.footer
    footer_para = footer.paragraphs[0]
    footer_para.alignment = WD_ALIGN_PARAGRAPH.LEFT
    f_tabs = footer_para.paragraph_format.tab_stops
    f_tabs.add_tab_stop(Cm(8), WD_ALIGN_PARAGRAPH.CENTER)
    f_tabs.add_tab_stop(Cm(16), WD_ALIGN_PARAGRAPH.RIGHT)
    fr1 = footer_para.add_run("© slohmaier.com")
    fr1.font.size = Pt(9)
    fr1.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    footer_para.add_run("\t")
    fr2 = footer_para.add_run(classification)
    fr2.font.size = Pt(9)
    fr2.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    fr2.bold = True
    footer_para.add_run("\t")
    fr3 = footer_para.add_run("Page ")
    fr3.font.size = Pt(9)
    fr3.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    add_field(footer_para, "PAGE", "1")
    fr4 = footer_para.add_run(" / ")
    fr4.font.size = Pt(9)
    fr4.font.color.rgb = RGBColor(0x59, 0x59, 0x59)
    add_field(footer_para, "NUMPAGES", "1")

    # --- First-page header/footer (cover): empty ---
    fp_header = section.first_page_header
    fp_header.paragraphs[0].text = ""
    fp_footer = section.first_page_footer
    fp_footer.paragraphs[0].text = ""


# ---------------------------------------------------------------------------
# Content
# ---------------------------------------------------------------------------

REPO_ROOT = Path(__file__).resolve().parent.parent
LOGO_PATH = REPO_ROOT / "branding" / "logo-doc-light.png"


def add_cover_page(doc):
    # Top logo (right-aligned, ~7 cm wide)
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    if LOGO_PATH.exists():
        run = p.add_run()
        run.add_picture(str(LOGO_PATH), width=Cm(7))
    else:
        r = p.add_run("[LOGO]")
        r.font.size = Pt(10)
        r.font.color.rgb = RGBColor(0x80, 0x80, 0x80)

    # Vertical space
    for _ in range(8):
        doc.add_paragraph()

    # Classification banner
    cls_p = doc.add_paragraph()
    cls_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    cls_r = cls_p.add_run("CONFIDENTIAL")
    cls_r.font.size = Pt(11)
    cls_r.font.bold = True
    cls_r.font.color.rgb = RGBColor(0xC5, 0x5A, 0x11)

    # Title
    title_p = doc.add_paragraph(style="Title")
    title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    title_p.add_run("<Document title>")

    # Subtitle
    sub_p = doc.add_paragraph(style="Subtitle")
    sub_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    sub_p.add_run("<Subtitle or short description>")

    for _ in range(4):
        doc.add_paragraph()

    # Metadata block
    meta_tbl = doc.add_table(rows=6, cols=2)
    meta_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
    meta_tbl.autofit = False
    meta_tbl.columns[0].width = Cm(4.5)
    meta_tbl.columns[1].width = Cm(8.5)
    meta_data = [
        ("Project", "<Project name>"),
        ("Document ID", "<DOC-ID>"),
        ("Version", "1.0"),
        ("Date", "YYYY-MM-DD"),
        ("Status", "Draft"),
        ("Classification", "Confidential"),
    ]
    for i, (k, v) in enumerate(meta_data):
        c1 = meta_tbl.cell(i, 0)
        c1.width = Cm(4.5)
        c1.text = ""
        c1_p = c1.paragraphs[0]
        c1_r = c1_p.add_run(k)
        c1_r.bold = True
        c1_r.font.size = Pt(11)
        set_cell_shading(c1, LIGHT_GREY)
        set_cell_borders(c1, SOFT_GREY)

        c2 = meta_tbl.cell(i, 1)
        c2.width = Cm(8.5)
        c2.text = ""
        c2_p = c2.paragraphs[0]
        c2_r = c2_p.add_run(v)
        c2_r.font.size = Pt(11)
        set_cell_borders(c2, SOFT_GREY)

    # Page break
    doc.add_page_break()


def add_document_control(doc):
    doc.add_heading("Document Control", level=1)
    doc.add_paragraph(
        "This page documents the formal control of this document per ISO 9001. "
        "Changes only take effect after the release process."
    )

    doc.add_heading("Approvals", level=2)
    appr_tbl = doc.add_table(rows=4, cols=4)
    appr_tbl.style = "Light Grid Accent 1"
    hdr = appr_tbl.rows[0].cells
    for i, h in enumerate(["Role", "Name", "Signature / Date", "Notes"]):
        hdr[i].text = ""
        r = hdr[i].paragraphs[0].add_run(h)
        r.bold = True
        set_cell_shading(hdr[i], LIGHT_GREY)
    roles = ["Author", "Reviewer", "Approver"]
    for i, role in enumerate(roles, start=1):
        appr_tbl.rows[i].cells[0].text = role
        for j in (1, 2, 3):
            appr_tbl.rows[i].cells[j].text = ""

    doc.add_heading("Revision History", level=2)
    rev_tbl = doc.add_table(rows=3, cols=4)
    rev_tbl.style = "Light Grid Accent 1"
    rev_hdr = rev_tbl.rows[0].cells
    for i, h in enumerate(["Version", "Date", "Change", "Author"]):
        rev_hdr[i].text = ""
        r = rev_hdr[i].paragraphs[0].add_run(h)
        r.bold = True
        set_cell_shading(rev_hdr[i], LIGHT_GREY)
    rev_data = [
        ("0.1", "YYYY-MM-DD", "Initial draft", "<Author>"),
        ("1.0", "YYYY-MM-DD", "First release", "<Author>"),
    ]
    for i, row in enumerate(rev_data, start=1):
        for j, v in enumerate(row):
            rev_tbl.rows[i].cells[j].text = v

    doc.add_heading("Distribution List", level=2)
    dist_tbl = doc.add_table(rows=3, cols=3)
    dist_tbl.style = "Light Grid Accent 1"
    dist_hdr = dist_tbl.rows[0].cells
    for i, h in enumerate(["Recipient", "Role", "Organisation"]):
        dist_hdr[i].text = ""
        r = dist_hdr[i].paragraphs[0].add_run(h)
        r.bold = True
        set_cell_shading(dist_hdr[i], LIGHT_GREY)
    for i in range(1, 3):
        for j in range(3):
            dist_tbl.rows[i].cells[j].text = ""

    doc.add_page_break()


def add_toc_pages(doc):
    doc.add_heading("Table of Contents", level=1)
    p = doc.add_paragraph()
    add_field(
        p,
        'TOC \\o "1-3" \\h \\z \\u',
        "Update TOC: F9 (right-click on TOC > Update Field)",
    )
    doc.add_page_break()

    doc.add_heading("List of Figures", level=1)
    p = doc.add_paragraph()
    add_field(
        p,
        'TOC \\h \\z \\c "Figure"',
        "Update list of figures: F9",
    )
    doc.add_page_break()

    doc.add_heading("List of Tables", level=1)
    p = doc.add_paragraph()
    add_field(
        p,
        'TOC \\h \\z \\c "Table"',
        "Update list of tables: F9",
    )
    doc.add_page_break()


def add_abbreviations(doc):
    doc.add_heading("Abbreviations", level=1)
    tbl = doc.add_table(rows=4, cols=2)
    tbl.style = "Light Grid Accent 1"
    hdr = tbl.rows[0].cells
    for i, h in enumerate(["Abbreviation", "Meaning"]):
        hdr[i].text = ""
        r = hdr[i].paragraphs[0].add_run(h)
        r.bold = True
        set_cell_shading(hdr[i], LIGHT_GREY)
    examples = [
        ("ASIL", "Automotive Safety Integrity Level"),
        ("ECU", "Electronic Control Unit"),
        ("MISRA", "Motor Industry Software Reliability Association"),
    ]
    for i, (k, v) in enumerate(examples, start=1):
        tbl.rows[i].cells[0].text = k
        tbl.rows[i].cells[1].text = v
    doc.add_page_break()


def add_main_content(doc):
    # Section 1
    doc.add_heading("1. Introduction", level=1)
    doc.add_heading("1.1 Purpose", level=2)
    doc.add_paragraph(
        "<Short paragraph: why does this document exist? What question does it answer?>"
    )
    doc.add_heading("1.2 Scope", level=2)
    doc.add_paragraph("<Which product, project, phase?>")
    doc.add_heading("1.3 Definitions", level=2)
    doc.add_paragraph(
        "<Domain-specific terms used in this document. Generic abbreviations are listed "
        "in the Abbreviations section.>"
    )
    doc.add_heading("1.4 References", level=2)
    ref_tbl = doc.add_table(rows=3, cols=3)
    ref_tbl.style = "Light Grid Accent 1"
    ref_hdr = ref_tbl.rows[0].cells
    for i, h in enumerate(["ID", "Title", "Version / Location"]):
        ref_hdr[i].text = ""
        r = ref_hdr[i].paragraphs[0].add_run(h)
        r.bold = True
        set_cell_shading(ref_hdr[i], LIGHT_GREY)
    for i in range(1, 3):
        for j in range(3):
            ref_tbl.rows[i].cells[j].text = ""

    # Section 2
    doc.add_heading("2. Main Content", level=1)
    doc.add_paragraph(
        "<This is where the document-type-specific content begins. This template "
        "provides structure and styles; concrete sections come from each document "
        "type (PID, QA Plan, SWE Plan, ...).>"
    )

    # Demonstrate styles
    doc.add_heading("2.1 Example: Styles", level=2)
    doc.add_paragraph(
        "Body text in the template. Calibri 11 with 1.15 line spacing."
    )

    code_p = doc.add_paragraph(style="Code")
    code_p.add_run(
        "// Example code in code style (Consolas 10)\n"
        "Status epb_apply(uint8_t force_percent);"
    )
    set_paragraph_shading(code_p, LIGHT_GREY)

    note_p = doc.add_paragraph(style="Note")
    note_p.add_run("NOTE: ").bold = True
    note_p.add_run("Note style for supplementary information.")
    set_paragraph_border(note_p, NOTE_BORDER, size=18, side="left")
    set_paragraph_shading(note_p, NOTE_BG)

    warn_p = doc.add_paragraph(style="Warning")
    warn_p.add_run("WARNING: ").bold = True
    warn_p.add_run("Warning style for safety-relevant information.")
    set_paragraph_border(warn_p, WARN_BORDER, size=18, side="left")
    set_paragraph_shading(warn_p, WARN_BG)

    req_p = doc.add_paragraph(style="Requirement")
    req_p.add_run("REQ-001: ").bold = True
    req_p.add_run(
        "Requirement style for inline requirements "
        "(usually in Markdown via Doorstop, but in Word for formal reports)."
    )
    set_paragraph_border(req_p, REQ_BORDER, size=18, side="left")
    set_paragraph_shading(req_p, REQ_BG)

    doc.add_heading("2.2 Example Table", level=2)
    tbl = doc.add_table(rows=4, cols=3)
    tbl.style = "Light Grid Accent 1"
    hdr = tbl.rows[0].cells
    for i, h in enumerate(["ID", "Description", "ASIL"]):
        hdr[i].text = ""
        r = hdr[i].paragraphs[0].add_run(h)
        r.bold = True
        set_cell_shading(hdr[i], LIGHT_GREY)
    rows = [
        ("F-01", "Apply on driver request", "D"),
        ("F-05", "Release on driver request", "B"),
        ("F-10", "HMI: LED control", "QM"),
    ]
    for i, row in enumerate(rows, start=1):
        for j, v in enumerate(row):
            tbl.rows[i].cells[j].text = v

    # Caption demo
    cap_p = doc.add_paragraph(style="Caption")
    cap_p.add_run("Table 1: Example requirements with ASIL classification")

    # Section 3 — Appendix
    doc.add_heading("3. Appendix", level=1)
    doc.add_paragraph(
        "<Appendices: detail calculations, scripts, extra tables, glossary.>"
    )


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

def build_template(out_path: Path):
    doc = Document()
    setup_page(doc)
    configure_styles(doc)
    build_header_footer(doc)

    add_cover_page(doc)
    add_document_control(doc)
    add_toc_pages(doc)
    add_abbreviations(doc)
    add_main_content(doc)

    out_path.parent.mkdir(parents=True, exist_ok=True)
    doc.save(out_path)
    print(f"Wrote: {out_path}")


if __name__ == "__main__":
    out = Path(sys.argv[1] if len(sys.argv) > 1 else
               Path(__file__).resolve().parent.parent / "templates-word" / "slohmaier-doc-template.docx")
    build_template(out)
