"""Dashboard query-param clamps (#39200 + #74778 salvage).

FastAPI Query bounds reject out-of-range values at the validation layer
(422) instead of letting them reach SQL/insights code: an unbounded
``limit`` drags every session row out of SQLite in one hit (multiplied
across every profile's state.db on the fan-out endpoint), and an
unbounded/inverted ``days`` forces full-history InsightsEngine work.
"""

from __future__ import annotations

from contextlib import closing

import pytest

fastapi = pytest.importorskip("fastapi")
from fastapi.testclient import TestClient


@pytest.fixture()
def client(tmp_path, monkeypatch):
    monkeypatch.setenv("HERMES_HOME", str(tmp_path))
    monkeypatch.setenv("HERMES_DASHBOARD_SESSION_TOKEN", "clamp-test-token")
    from hermes_cli import web_server
    import hermes_state

    monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", tmp_path / "state.db")
    # The owner initializes storage, not GET requests or an unrelated lifespan.
    with closing(hermes_state.SessionDB()) as db:
        db.create_session("clamp-session", source="cli")

    with closing(TestClient(web_server.app, raise_server_exceptions=False)) as c:
        c.headers["Authorization"] = "Bearer clamp-test-token"
        yield c


class TestSessionPaginationClamps:
    def test_oversized_limit_rejected(self, client):
        r = client.get("/api/sessions", params={"limit": 10_000})
        assert r.status_code == 422


    def test_profile_fanout_limit_clamped(self, client):
        r = client.get("/api/profiles/sessions", params={"limit": 10_000})
        assert r.status_code == 422

    def test_profile_fanout_accepts_real_desktop_maximum(self, client):
        # Desktop callers use limit=200 (ARCHIVED_FETCH_LIMIT, command
        # palette) and electron over-fetches limit+offset — the clamp must
        # sit ABOVE real client maxima, not break them.
        r = client.get("/api/profiles/sessions", params={"limit": 200, "offset": 120})
        assert r.status_code == 200

    def test_in_range_limit_accepted(self, client):
        r = client.get("/api/sessions", params={"limit": 50})
        assert r.status_code == 200
        assert r.json()["limit"] == 50
        assert [row["id"] for row in r.json()["sessions"]] == ["clamp-session"]


class TestAnalyticsDaysClamps:
    @pytest.mark.parametrize("days", [0, -5, 100_000])
    def test_out_of_range_days_rejected(self, client, days):
        r = client.get("/api/analytics/usage", params={"days": days})
        assert r.status_code == 422

    def test_in_range_days_accepted(self, client):
        r = client.get("/api/analytics/usage", params={"days": 30})
        assert r.status_code == 200
