"""Complete solution-explanation block: instruction + two bar models + working.

This is the visible Singapore-Math solution layout: an instruction line, a
SETUP bar model (blank unit-boxes), a SOLVED bar model (units filled in
once known), the list of working steps, and the final answer.

IMPORTANT — algebra schema is captured NOW, rendered LATER. The data shape
makes room for a future P5–P6 algebra toggle, so the data doesn't need a
painful retrofit when that sprint lands:

  algebra = {
    "variables": [{"symbol": "u", "meaning": "1 unit"}, ...],
    "entity_expressions": [
        {"label": "Purple puppets", "expression": "6u"},
        {"label": "Yellow puppets", "expression": "5u + 7"},
    ],
    "equations": [
        "6u + (5u + 7) = 39",
        "11u + 7 = 39",
        "11u = 32",
        "u = 32/11",
    ],
  }

Each `working_steps` entry can be either a plain string OR a dict
  {"text": "1 unit = 39 − 35 = 4", "algebra": "u = 4"}
— the algebra side is preserved on the SVG element (`data-algebra` attr)
so renderers can swap modes without re-running the agent.

generate(instruction, setup, solved, working_steps, answer, *,
         algebra=None, target_width=860) -> str
"""

import bar_model  # sibling module
import equation_line  # noqa: F401  (kept for parity / standalone use)

STYLE = bar_model.STYLE
PAD = 24
SECT_GAP = 14          # vertical gap before next section
STEP_LH = 28           # line height inside the working-steps section
ANSWER_LH = 36

DEFAULT_W = 860


def _esc(s):
    return (str(s).replace('&', '&amp;').replace('<', '&lt;')
            .replace('>', '&gt;'))


def _instruction_content(text, *, x0, y0, w):
    h = 36
    inner = (
        f'<text x="{x0 + PAD}" y="{y0 + h/2:.1f}" '
        f'font-family="sans-serif" font-size="14" '
        f'font-style="italic" fill="var(--ink)" '
        f'dominant-baseline="central">{_esc(text)}</text>'
    )
    return inner, h


def _working_steps_content(steps, answer, *, x0, y0, w):
    out = []
    y = y0 + PAD
    for raw in steps:
        if isinstance(raw, dict):
            text = raw.get('text', '')
            alg = raw.get('algebra')
        else:
            text = str(raw); alg = None
        out.append(
            f'<text x="{x0 + PAD}" y="{y:.1f}" '
            f'font-family="ui-monospace, Menlo, Consolas, monospace" '
            f'font-size="15" fill="var(--ink)" '
            f'dominant-baseline="central" '
            f'data-algebra="{_esc(alg) if alg else ""}">{_esc(text)}</text>'
        )
        y += STEP_LH
    # final answer (bold crimson)
    y += 6
    out.append(
        f'<line x1="{x0 + PAD}" y1="{y - 4:.1f}" '
        f'x2="{x0 + w - PAD}" y2="{y - 4:.1f}" '
        f'stroke="var(--border)" stroke-width="1"/>'
    )
    y += 10
    out.append(
        f'<text x="{x0 + PAD}" y="{y:.1f}" font-family="sans-serif" '
        f'font-size="18" font-weight="bold" fill="var(--crimson)" '
        f'dominant-baseline="central">Answer: {_esc(answer)}</text>'
    )
    y += PAD
    return '\n'.join(out), (y - y0)


def _bm_content(params, *, x0, y0, w):
    """Dispatch to the right bar_model inner-content helper based on params."""
    t = params.get('type', 'unitary')
    if t == 'unitary':
        return bar_model._unitary_content(
            params['entities'],
            total=params.get('total'),
            unit_value=params.get('unit_value'),
            target_width=w, x0=x0, y0=y0,
        )
    # parts-based (part_whole, comparison, multiplication)
    return bar_model._parts_content(
        params['entities'], target_width=w, x0=x0, y0=y0,
    )


def generate(instruction, setup, solved, working_steps, answer, *,
             algebra=None, target_width=DEFAULT_W):
    """Compose one solution block.

    setup / solved: dicts of bar_model params. Either
      {"type":"unitary", "entities":[...], "total":..., "unit_value":...}
    or
      {"type":"part_whole"|"comparison"|"multiplication", "entities":[...]}

    `algebra`: optional dict — see module docstring.  Captured on the root
    SVG element as `data-algebra` (JSON-encoded) so a future algebra-toggle
    renderer can pick it up without re-running ingestion.
    """
    import json

    # ---- pass 1: collect sections + per-section dimensions
    sections = []   # list of (xml_so_far, height) — we'll re-render with final width

    # measure each section first
    instr_w = target_width
    _, instr_h = _instruction_content(instruction, x0=0, y0=0, w=instr_w)

    _, setup_w, setup_h = _bm_content(setup, x0=0, y0=0, w=target_width)
    _, solved_w, solved_h = _bm_content(solved, x0=0, y0=0, w=target_width)

    # working-steps height
    n_steps = len(working_steps)
    work_h = PAD * 2 + n_steps * STEP_LH + ANSWER_LH + 20

    W = max(target_width, setup_w, solved_w)
    H = PAD + instr_h + SECT_GAP + setup_h + SECT_GAP + solved_h + SECT_GAP + work_h + PAD

    # ---- pass 2: render at the final width so dividers match
    body = []
    y = PAD
    inner, h = _instruction_content(instruction, x0=0, y0=y, w=W)
    body.append(inner); y += h
    # divider
    body.append(
        f'<line x1="0" y1="{y + SECT_GAP/2:.1f}" x2="{W}" '
        f'y2="{y + SECT_GAP/2:.1f}" stroke="var(--border)" stroke-width="1"/>'
    )
    y += SECT_GAP

    inner, _, h = _bm_content(setup, x0=0, y0=y, w=W)
    body.append(inner); y += h
    body.append(
        f'<line x1="0" y1="{y + SECT_GAP/2:.1f}" x2="{W}" '
        f'y2="{y + SECT_GAP/2:.1f}" stroke="var(--border)" stroke-width="1"/>'
    )
    y += SECT_GAP

    inner, _, h = _bm_content(solved, x0=0, y0=y, w=W)
    body.append(inner); y += h
    body.append(
        f'<line x1="0" y1="{y + SECT_GAP/2:.1f}" x2="{W}" '
        f'y2="{y + SECT_GAP/2:.1f}" stroke="var(--border)" stroke-width="1"/>'
    )
    y += SECT_GAP

    inner, h = _working_steps_content(working_steps, answer, x0=0, y0=y, w=W)
    body.append(inner); y += h

    H = y + PAD

    # Outer SVG with the algebra payload on the root element.
    alg_attr = ''
    if algebra is not None:
        alg_attr = f' data-algebra=\'{_esc(json.dumps(algebra, ensure_ascii=False))}\''
    parts = [
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{W:.0f}" '
        f'height="{H:.0f}" viewBox="0 0 {W:.0f} {H:.0f}"{alg_attr}>',
        STYLE,
        f'<rect x="3" y="3" width="{W-6:.0f}" height="{H-6:.0f}" rx="10" '
        f'fill="none" stroke="var(--border)" stroke-width="3"/>',
        '\n'.join(body),
        '</svg>',
    ]
    return '\n'.join(parts)


if __name__ == '__main__':
    import os
    out = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'samples'))
    os.makedirs(out, exist_ok=True)

    # The puppets sample from the spec.
    entities = [
        {"label": "Purple puppets", "units": 6, "extra": 0,
         "expression": "6u"},
        {"label": "Yellow puppets", "units": 5, "extra": 1, "extra_value": 7,
         "expression": "5u + 7"},
    ]
    block = generate(
        instruction="Draw 6 boxes for purple and 5 boxes + 1 known block for yellow.",
        setup={"type": "unitary", "entities": entities, "total": 39,
               "unit_value": None},
        solved={"type": "unitary", "entities": entities, "total": 39,
                "unit_value": 4},
        working_steps=[
            {"text": "5 × 7 = 35", "algebra": "5 × 7 = 35"},
            {"text": "1 unit = 39 − 35 = 4", "algebra": "u = (39 − 35) / 1 = 4"},
            {"text": "Number of purple puppets"},
            {"text": "= 6 units"},
            {"text": "= 6 × 4", "algebra": "= 6 · u"},
            {"text": "= 24", "algebra": "= 24"},
        ],
        answer=24,
        algebra={
            "variables": [{"symbol": "u", "meaning": "1 unit"}],
            "entity_expressions": [
                {"label": "Purple puppets", "expression": "6u"},
                {"label": "Yellow puppets", "expression": "5u + 7"},
            ],
            "equations": [
                "6u + (5u + 7) = 39",
                "11u + 7 = 39",
                "Note: the stated working uses 5×7 to peel off the extra block "
                "before solving; alternate form preserved for the toggle.",
            ],
        },
    )
    with open(os.path.join(out, 'solution_block_unitary.svg'), 'w',
              encoding='utf-8') as f:
        f.write(block)
    print('wrote solution_block_unitary.svg')
