"""Designer styling — per-part styles and custom CSS on page blocks, form
fields and whole forms: saved as set, and held to values that can't break
out of the stylesheet they end up in."""

STYLES = {
    "root": {"background_color": "#fef3c7", "border_width": 1, "border_radius": 8, "padding": "8px 12px"},
    "th": {"background_color": "#0f172a", "color": "#ffffff", "font_weight": "700", "text_align": "center"},
    "tr_alt": {"background_color": "#f8fafc"},
    "td": {"padding": "4 8", "vertical_align": "top"},
}
CSS = "border-left: 4px solid #2563eb;\nth { letter-spacing: .05em; }\n&:hover { opacity: .9 }"


async def test_page_block_styles_saved(client, auth_headers):
    block = {"id": "t1", "type": "table", "table_columns": ["A"], "table_rows": [["1"]], "styles": STYLES, "custom_css": CSS}
    resp = await client.post("/pages", headers=auth_headers, json={"name": "Styled", "blocks": [block]})
    assert resp.status_code == 201, resp.text
    saved = resp.json()["blocks"][0]
    # Only what was set comes back — no null for every other property.
    assert saved["styles"]["th"] == STYLES["th"]
    assert saved["styles"]["root"] == {**STYLES["root"], "border_width": 1.0, "border_radius": 8.0}
    assert saved["custom_css"] == CSS


async def test_form_field_and_form_styles_saved(client, auth_headers):
    fields = [{"name": "name", "type": "text", "label": "Name", "styles": {"label": {"color": "#b91c1c", "text_transform": "uppercase"}, "input": {"border_radius": 0}}}]
    settings = {"styles": {"root": {"font_family": "Georgia, serif"}}, "custom_css": "label { font-weight: 600 }"}
    resp = await client.post("/forms", headers=auth_headers, json={"name": "Styled form", "field_definitions": fields, "settings": settings})
    assert resp.status_code == 201, resp.text
    form = resp.json()
    assert form["field_definitions"][0]["styles"] == {"label": {"color": "#b91c1c", "text_transform": "uppercase"}, "input": {"border_radius": 0.0}}
    assert form["settings"]["styles"] == {"root": {"font_family": "Georgia, serif"}}
    assert form["settings"]["custom_css"] == "label { font-weight: 600 }"


async def test_values_that_could_escape_are_refused(client, auth_headers):
    bad_blocks = [
        {"id": "a", "type": "text", "styles": {"root": {"color": "red; } body { display: none"}}},
        {"id": "b", "type": "text", "styles": {"td": {"padding": "1px; background: url(x)"}}},
        {"id": "c", "type": "text", "styles": {"nowhere": {"color": "red"}}},
        {"id": "d", "type": "text", "custom_css": "</style><script>alert(1)</script>"},
        {"id": "e", "type": "text", "custom_css": "@import url(https://evil.example/x.css);"},
    ]
    for block in bad_blocks:
        resp = await client.post("/pages", headers=auth_headers, json={"name": "Bad", "blocks": [block]})
        assert resp.status_code == 422, (block, resp.text)
