"""Tests for Optional parameter handling."""

from typing import Optional

from starlette.testclient import TestClient

from crimsy import Crimsy, Header, Query, Router


async def test_optional_query_string() -> None:
    """Test Optional[str] query parameter."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/user")
    async def get_user(
        name: Optional[str] = Query(default=None),
    ) -> dict[str, str | None]:
        return {"name": name}

    app.add_router(router)
    client = TestClient(app)

    # Test with value
    response = client.get("/api/user?name=alice")
    assert response.status_code == 200
    assert response.json() == {"name": "alice"}

    # Test without value
    response = client.get("/api/user")
    assert response.status_code == 200
    assert response.json() == {"name": None}


async def test_optional_query_int() -> None:
    """Test Optional[int] query parameter."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/item")
    async def get_item(
        item_id: Optional[int] = Query(default=None),
    ) -> dict[str, int | None]:
        return {"item_id": item_id}

    app.add_router(router)
    client = TestClient(app)

    # Test with value
    response = client.get("/api/item?item_id=42")
    assert response.status_code == 200
    assert response.json() == {"item_id": 42}

    # Test without value
    response = client.get("/api/item")
    assert response.status_code == 200
    assert response.json() == {"item_id": None}


async def test_optional_query_float() -> None:
    """Test Optional[float] query parameter."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/score")
    async def get_score(
        value: Optional[float] = Query(default=None),
    ) -> dict[str, float | None]:
        return {"value": value}

    app.add_router(router)
    client = TestClient(app)

    # Test with value
    response = client.get("/api/score?value=98.5")
    assert response.status_code == 200
    assert response.json() == {"value": 98.5}

    # Test without value
    response = client.get("/api/score")
    assert response.status_code == 200
    assert response.json() == {"value": None}


async def test_optional_query_bool() -> None:
    """Test Optional[bool] query parameter."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/status")
    async def get_status(
        active: Optional[bool] = Query(default=None),
    ) -> dict[str, bool | None]:
        return {"active": active}

    app.add_router(router)
    client = TestClient(app)

    # Test with true value
    response = client.get("/api/status?active=true")
    assert response.status_code == 200
    assert response.json() == {"active": True}

    # Test with false value
    response = client.get("/api/status?active=false")
    assert response.status_code == 200
    assert response.json() == {"active": False}

    # Test without value
    response = client.get("/api/status")
    assert response.status_code == 200
    assert response.json() == {"active": None}


async def test_optional_header() -> None:
    """Test Optional[str] header parameter."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/auth")
    async def check_auth(
        api_key: Optional[str] = Header(default=None),
    ) -> dict[str, str | None]:
        return {"api_key": api_key}

    app.add_router(router)
    client = TestClient(app)

    # Test with header
    response = client.get("/api/auth", headers={"api-key": "secret123"})
    assert response.status_code == 200
    assert response.json() == {"api_key": "secret123"}

    # Test without header
    response = client.get("/api/auth")
    assert response.status_code == 200
    assert response.json() == {"api_key": None}


async def test_optional_query_with_default() -> None:
    """Test Optional query parameter with a non-None default."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/greet")
    async def greet(name: Optional[str] = Query(default="guest")) -> dict[str, str]:
        return {"message": f"Hello, {name}!"}

    app.add_router(router)
    client = TestClient(app)

    # Test with value
    response = client.get("/api/greet?name=alice")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello, alice!"}

    # Test without value (should use default)
    response = client.get("/api/greet")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello, guest!"}


async def test_optional_post_query() -> None:
    """Test Optional query parameter with POST method."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.post("/submit")
    async def submit(
        name: Optional[str] = Query(default=None),
    ) -> dict[str, str | None]:
        return {"name": name}

    app.add_router(router)
    client = TestClient(app)

    # Test with value
    response = client.post("/api/submit?name=testuser")
    assert response.status_code == 200
    assert response.json() == {"name": "testuser"}

    # Test without value
    response = client.post("/api/submit")
    assert response.status_code == 200
    assert response.json() == {"name": None}


async def test_multiple_optional_params() -> None:
    """Test multiple Optional parameters in one endpoint."""
    app = Crimsy()
    router = Router(prefix="/api")

    @router.get("/search")
    async def search(
        query: Optional[str] = Query(default=None),
        page: Optional[int] = Query(default=None),
        limit: Optional[int] = Query(default=None),
    ) -> dict[str, str | int | None]:
        return {"query": query, "page": page, "limit": limit}

    app.add_router(router)
    client = TestClient(app)

    # Test with all values
    response = client.get("/api/search?query=test&page=1&limit=10")
    assert response.status_code == 200
    assert response.json() == {"query": "test", "page": 1, "limit": 10}

    # Test with some values
    response = client.get("/api/search?query=test&page=2")
    assert response.status_code == 200
    assert response.json() == {"query": "test", "page": 2, "limit": None}

    # Test without values
    response = client.get("/api/search")
    assert response.status_code == 200
    assert response.json() == {"query": None, "page": None, "limit": None}
