#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Create five original A4 vector aids; PDFs are exported from these SVGs."""
from __future__ import annotations

import html
import textwrap
from pathlib import Path

ROOT = Path(__file__).resolve().parent
INK = "#15384b"
TEAL = "#006878"
PALE = "#e8f4f2"
LINE = "#536d76"
FONT = "DejaVu Sans, sans-serif"


def esc(value: str) -> str:
    return html.escape(value, quote=True)


def text(x: float, y: float, value: str, size: float = 4, bold: bool = False, colour: str = INK) -> str:
    weight = ' font-weight="700"' if bold else ""
    return f'<text x="{x}" y="{y}" font-family="{FONT}" font-size="{size}"{weight} fill="{colour}">{esc(value)}</text>'


def wrapped(x: float, y: float, value: str, width: int, size: float = 4, gap: float = 6.2) -> str:
    return "\n".join(text(x, y + i * gap, line, size) for i, line in enumerate(textwrap.wrap(value, width=width)))


def rect(x: float, y: float, width: float, height: float, fill: str = "#fff", stroke: str = LINE, radius: float = 2) -> str:
    return f'<rect x="{x}" y="{y}" width="{width}" height="{height}" rx="{radius}" fill="{fill}" stroke="{stroke}" stroke-width=".8"/>'


def panel(x: float, y: float, width: float, height: float, label: str) -> str:
    return "\n".join(
        [
            rect(x, y, width, height),
            f'<rect x="{x}" y="{y}" width="{width}" height="12" fill="{PALE}"/>',
            text(x + 5, y + 8.3, label, 4.8, True),
        ]
    )


def page(title: str, description: str, heading: str, subtitle: str, body: str) -> str:
    start = (
        f'<svg xmlns="http://www.w3.org/2000/svg" width="210mm" height="297mm" viewBox="0 0 210 297" '
        f'role="img" aria-labelledby="title desc"><title id="title">{esc(title)}</title>'
        f'<desc id="desc">{esc(description)}</desc><rect width="210" height="297" fill="#fff"/>'
    )
    header = "\n".join(
        [
            text(14, 17, "SubjectNest  /  Foundation Science  /  Weeks 1–2", 4.2, True, TEAL),
            text(14, 30, heading, 7.6, True),
            f'<path d="M14 37 H196" stroke="{TEAL}" stroke-width="1.2"/>',
            text(14, 46, subtitle, 3.8),
        ]
    )
    footer = "\n".join(
        [
            f'<path d="M14 277 H196" stroke="{LINE}" stroke-width=".6"/>',
            text(14, 286, "Point · say · sign · use AAC · direct an adult · choose no touch.", 3.3),
            text(14, 293, "Original SubjectNest vector  •  © 2026 NeuroForgeIO Pty Ltd  •  CC BY 4.0", 2.9),
        ]
    )
    return f"{start}\n{header}\n{body}\n{footer}\n</svg>\n"


def write(name: str, content: str) -> None:
    (ROOT / f"{name}.svg").write_text(content, encoding="utf-8")


routine = (
    ("1 · LOOK", "Use an accessible safe route. Adult report is labelled as a report."),
    ("2 · ASK", "What can we check about this exact source?"),
    ("3 · PREDICT", "Say what you think before any try. It is not a result."),
    ("4 · TRY SAFELY", "Teacher approves object and action. No forced touch."),
    ("5 · RECORD", "Write actual source, date, action, result or NOT TESTED."),
    ("6 · SHARE OR KEEP PRIVATE", "Explain one clue and one thing still unknown."),
)
routine_parts = []
for index, (label, sentence) in enumerate(routine):
    y = 55 + index * 35
    routine_parts.extend([panel(14, y, 182, 31, label), wrapped(20, y + 23, sentence, 85, 3.5, 5.8)])
write(
    "safe-notice",
    page(
        "Safe-notice routine, six original steps",
        "Six ordered steps: LOOK using an accessible safe route; ASK; PREDICT before trying; TRY SAFELY with teacher approval; RECORD source, date and actual result or NOT TESTED; SHARE OR KEEP PRIVATE with one clue and unknown. Adult-reported descriptions are attributed; no object result is prefilled.",
        "Notice safely · record honestly",
        "A process poster. It does not say an investigation happened.",
        "\n".join(routine_parts),
    ),
)

observation_labels = (
    "SOURCE + LOCAL DATE",
    "OBJECT + VERIFIED MATERIAL OR UNKNOWN",
    "ACCESS: MY DIRECT SENSE / ADULT REPORT / MODEL",
    "ACTUAL FEATURE NOTICED OR REPORTED",
    "ACTION + RESULT · OR NOT TESTED",
    "STILL UNKNOWN + NEXT QUESTION",
)
observation_parts = [panel(14, 55 + index * 35, 182, 31, label) for index, label in enumerate(observation_labels)]
write(
    "observation-log",
    page(
        "Blank actual-source observation and provenance log",
        "Six blank fields in order: source and local date; object and verified material or UNKNOWN; access as child's direct sense, adult report or model; feature noticed/reported; action and result or NOT TESTED; still unknown and next question. No result or child data is supplied.",
        "What did this source show?",
        "Fill from an actual local source. Mark a model as MODEL.",
        "\n".join(observation_parts),
    ),
)

prediction_labels = (
    "QUESTION",
    "MY PREDICTION · BEFORE THE TRY",
    "OBJECT + VERIFIED SOURCE + DATE",
    "WHAT WE ACTUALLY DID AND SAW · OR NOT TESTED",
    "SAME / DIFFERENT / NOT TESTED",
    "STILL TO CHECK",
)
prediction_parts = [panel(14, 55 + index * 35, 182, 31, label) for index, label in enumerate(prediction_labels)]
write(
    "prediction-check",
    page(
        "Blank prediction versus actual observation mat",
        "Six blank fields: question; prediction before try; object verified source and date; actual action and result or NOT TESTED; same, different or not tested; still to check. A prediction is never prefilled as an actual result.",
        "I think · we tried · we saw",
        "Keep prediction and actual result in different boxes.",
        "\n".join(prediction_parts),
    ),
)

model_parts = [
    text(14, 57, "MODEL CARD · INVENTED DRAWING ONLY", 4.3, True, TEAL),
    rect(30, 72, 150, 163, "#fff", INK, 0),
    f'<path d="M83 119 H127" fill="none" stroke="{INK}" stroke-width="1.5"/>',
    f'<ellipse cx="105" cy="162" rx="36" ry="23" fill="#fff" stroke="{INK}" stroke-width="1.5"/>',
    wrapped(16, 251, "These drawn lines do not show actual material, a hole, texture or a bend result.", 87, 3.6, 5.7),
]
write(
    "model-not-object",
    page(
        "Invented model card, not a real object observation",
        "One broad rectangle outline; inside is one large oval outline and one short straight horizontal line above the oval. It is an invented model drawing. No real material, texture, hole, scale, thickness, bend result, place or safe-use claim is shown.",
        "A drawing is a model",
        "Describe exact marks. Leave real-object properties unknown.",
        "\n".join(model_parts),
    ),
)

representation_labels = (
    "SOURCE + DATE · OR MODEL-USE DATE",
    "FEATURE: DIRECT / ADULT REPORT / NOT AVAILABLE",
    "MY MARK / DRAWING SHOWS · EXPLAIN KEY",
    "WHAT THIS REPRESENTATION CANNOT TELL",
    "ONE NEXT QUESTION TO CHECK SAFELY",
)
representation_parts = [panel(14, 55 + index * 43, 182, 38, label) for index, label in enumerate(representation_labels)]
write(
    "representation-record",
    page(
        "Blank source-aware representation record",
        "Five blank fields: source and date or model-use date; feature classed as directly observed, adult reported or not available; child's mark or drawing shows with an explained key; what the representation cannot tell; one safe next question. A model mark is not local observation data.",
        "What does my mark stand for?",
        "A symbol needs a source. Keep actual and model apart.",
        "\n".join(representation_parts),
    ),
)

key_parts = [
    panel(14, 55, 182, 59, "STRAIGHT EDGE SEEN  ·  ▭"),
    rect(28, 78, 42, 22, "#fff", INK, 0),
    wrapped(80, 83, "Use only for a straight edge in the named source. A drawn rectangle is not a real object.", 43, 3.7, 5.7),
    panel(14, 121, 182, 59, "CURVED EDGE SEEN  ·  ◯"),
    f'<circle cx="49" cy="150" r="13" fill="#fff" stroke="{INK}" stroke-width="1.5"/>',
    wrapped(80, 148, "Use only for a curved edge in the named source. Keep actual and drawing apart.", 43, 3.7, 5.7),
    panel(14, 187, 182, 59, "NOT CHECKED  ·  ?"),
    text(38, 225, "?", 19, True),
    wrapped(80, 214, "This means unknown or not yet checked. It does not mean false.", 43, 3.7, 5.7),
    wrapped(16, 259, "Temporary classroom key. Not an official safety sign. Add source and date.", 88, 3.4, 5.3),
]
write(
    "symbol-key",
    page(
        "Original temporary Science observation symbol key",
        "Three large labelled rows: outline rectangle stands for a straight edge seen, outline circle stands for a curved edge seen, question mark stands for NOT CHECKED. These are local classroom record marks, not official safety symbols. Source and date are required before an actual-observation mark.",
        "Our temporary observation key",
        "Explain each mark. Source and date decide what it can say.",
        "\n".join(key_parts),
    ),
)
