"""Repeating line items ("subform" fields): each row is shape-checked,
required/validated like a small form, row formulas are computed before the
form's own (so a total can SUM them), and min/max rows are enforced."""

ITEMS = {
    "name": "items",
    "type": "subform",
    "label": "Items",
    "min_rows": 1,
    "max_rows": 3,
    "subform_fields": [
        {"name": "product", "type": "text", "label": "Product", "required": True},
        {"name": "qty", "type": "number", "label": "Qty", "validation_rules": [{"type": "min_value", "value": 1}]},
        {"name": "price", "type": "number", "label": "Price"},
        {"name": "amount", "type": "formula", "label": "Amount", "formula_expression": "qty * price * (1 - parent.discount / 100)"},
    ],
}
FIELDS = [
    {"name": "discount", "type": "number", "label": "Discount %"},
    ITEMS,
    {"name": "total", "type": "formula", "label": "Total", "formula_expression": "SUM(items.amount)"},
]


async def _form(client, headers):
    form = (await client.post("/forms", headers=headers, json={"name": "Order", "field_definitions": FIELDS})).json()
    await client.post(f"/forms/{form['form_id']}/publish", headers=headers)
    return form


async def _submit(client, headers, form, values):
    return await client.post("/form-submissions", headers=headers, json={"form_id": form["form_id"], "form_version": 1, "field_values": values})


def test_subform_definition_rules():
    from pydantic import ValidationError

    from app.schemas.workflow import FormFieldDef

    import pytest

    with pytest.raises(ValidationError):
        FormFieldDef(name="items", type="subform", label="Items")  # no row fields
    with pytest.raises(ValidationError):
        FormFieldDef(name="items", type="subform", label="Items", subform_fields=[{"name": "x", "type": "subform", "label": "X", "subform_fields": [{"name": "y", "type": "text", "label": "Y"}]}])


async def test_line_items_are_computed_and_checked(client, auth_headers):
    form = await _form(client, auth_headers)
    rows = [{"product": "Pen", "qty": 2, "price": 10, "amount": 999}, {"product": "Pad", "qty": "3", "price": 5}]
    resp = await _submit(client, auth_headers, form, {"discount": 10, "items": rows})
    assert resp.status_code == 201, resp.text
    values = resp.json()["field_values"]
    # Client-sent row formula values are replaced; "3" is normalized to 3.
    assert [r["amount"] for r in values["items"]] == [18, 13.5]
    assert values["items"][1]["qty"] == 3
    assert values["total"] == 31.5

    def first_error(r):
        return r.json()["detail"][0]["msg"]

    missing = await _submit(client, auth_headers, form, {"items": [{"qty": 1}]})
    assert missing.status_code == 422 and first_error(missing) == "Items: Row 1: Product is required."
    rule = await _submit(client, auth_headers, form, {"items": [{"product": "Pen"}, {"product": "Pad", "qty": 0}]})
    assert rule.status_code == 422 and first_error(rule).startswith("Items: Row 2: Qty")
    shape = await _submit(client, auth_headers, form, {"items": [{"product": "Pen", "qty": "lots"}]})
    assert shape.status_code == 422 and "row 1" in first_error(shape).lower() and "Qty" in first_error(shape)
    not_list = await _submit(client, auth_headers, form, {"items": "Pen"})
    assert not_list.status_code == 422
    too_few = await _submit(client, auth_headers, form, {"items": []})
    assert too_few.status_code == 422 and first_error(too_few) == "Items: Add at least 1 row."
    too_many = await _submit(client, auth_headers, form, {"items": [{"product": str(i)} for i in range(4)]})
    assert too_many.status_code == 422 and first_error(too_many) == "Items: At most 3 rows."
