import pytest
from flask.testing import FlaskClient

from flask_jeroboam import Blueprint, Jeroboam
from flask_jeroboam.models import InboundModel
from flask_jeroboam.view_arguments.functions import Body

response_not_valid_int = {
    "detail": [
        {
            "loc": ["body", "payload"],
            "msg": "Input should be a valid integer, unable to parse string as an integer",
            "type": "int_parsing",
        }
    ]
}


def _valid(value) -> dict:
    """Valid function."""
    return {"payload": value}


@pytest.mark.parametrize(
    "url,body_value,expected_status,expected_response",
    [
        ("/body/str", {"payload": "foobar"}, 201, _valid("foobar")),
        ("/body/int", {"payload": 123}, 201, _valid(123)),
        ("/body/int", {"payload": "not_a_valid_int"}, 400, response_not_valid_int),
        (
            "/body/base_model",
            {"page": 1, "type": "item"},
            201,
            {"page": 1, "type": "item"},
        ),
    ],
)
def test_post_body_operations(
    client: FlaskClient,
    url: str,
    body_value: dict,
    expected_status: int,
    expected_response: dict,
):
    """Testing Various GET operations with query parameters.

    GIVEN a GET endpoint configiured with query parameters
    WHEN a request is made to the endpoint
    THEN the request is parsed and validated accordingly
    """
    response = client.post(url, json=body_value)
    assert response.json == expected_response
    assert response.status_code == expected_status


def test_post_body_list_of_base_model(
    one_shot_app: Jeroboam, one_shot_client: FlaskClient
):
    """Test Body Parameter with POST method."""

    class InBound(InboundModel):
        """Inbound model."""

        item: str
        count: int

    @one_shot_app.post("/body/list_non_scalar", response_model=list[InBound])
    def post_body_list_non_scalar(payload: list[InBound] = Body()):
        return payload

    response = one_shot_client.post(
        "/body/list_non_scalar",
        json=[{"item": "foobar", "count": 1}, {"item": "bar", "count": 3}],
    )
    assert response.json == [
        {"item": "foobar", "count": 1},
        {"item": "bar", "count": 3},
    ]
    assert response.status_code == 201


def test_put_body_with_empty_string_rule(
    one_shot_app: Jeroboam, one_shot_client: FlaskClient
):
    """Regression test for #110: body field fails to build when URL rule is empty string."""
    bp = Blueprint("wines", __name__, url_prefix="/wines")

    @bp.put("")
    def put_empty_rule(payload: int):
        return {"payload": payload}

    one_shot_app.register_blueprint(bp)
    response = one_shot_client.put("/wines", json={"payload": 42})
    assert response.status_code == 201
    assert response.json == {"payload": 42}


def test_put_multi_body_with_empty_string_rule(
    one_shot_app: Jeroboam, one_shot_client: FlaskClient
):
    """Regression test for #110: multi-arg body (create_model path) with empty string rule."""
    bp = Blueprint("spirits", __name__, url_prefix="/spirits")

    @bp.put("")
    def put_empty_rule_multi(qty: int, name: str):
        return {"qty": qty, "name": name}

    one_shot_app.register_blueprint(bp)
    response = one_shot_client.put("/spirits", json={"qty": 5, "name": "cognac"})
    assert response.status_code == 201
    assert response.json == {"qty": 5, "name": "cognac"}
