"""Integration coverage for the Dashboard Designer's widened Widget/Dashboard
API — config round-tripping (axis config, icon, filter wiring), the "filter"
widget type, and the new /datasets/query-series endpoint (Phase 1/3)."""


async def test_dashboard_accepts_parameters_field(client, auth_headers):
    resp = await client.post("/dashboards", headers=auth_headers, json={"name": "Sales Overview"})
    assert resp.status_code == 201
    assert resp.json()["parameters"] == []


async def test_widget_config_round_trips(client, auth_headers):
    dash_resp = await client.post("/dashboards", headers=auth_headers, json={"name": "Sales Overview"})
    dashboard_id = dash_resp.json()["id"]

    master_resp = await client.post(
        "/masters",
        headers=auth_headers,
        json={
            "name": "Orders",
            "field_schema": [
                {"name": "status", "type": "text"},
                {"name": "revenue", "type": "number"},
                {"name": "placed_at", "type": "date"},
            ],
        },
    )
    master_id = master_resp.json()["id"]

    widget_resp = await client.post(
        f"/dashboards/{dashboard_id}/widgets",
        headers=auth_headers,
        json={
            "type": "bar_chart",
            "data_source": f"master:{master_id}",
            "color": "#4f46e5",
            "config": {
                "x_field": "status",
                "x_bucket": "none",
                "series": [{"field": "revenue", "aggregate": "sum", "label": "Revenue"}],
            },
        },
    )
    assert widget_resp.status_code == 201, widget_resp.text
    body = widget_resp.json()
    assert body["config"]["x_field"] == "status"
    assert body["config"]["series"][0]["aggregate"] == "sum"

    widget_id = body["id"]
    kpi_resp = await client.post(
        f"/dashboards/{dashboard_id}/widgets",
        headers=auth_headers,
        json={
            "type": "kpi_card",
            "data_source": f"master:{master_id}",
            "color": "#10b981",
            "config": {"icon": "TrendingUp", "expression": "SUM(revenue) / COUNT()"},
        },
    )
    assert kpi_resp.status_code == 201, kpi_resp.text
    assert kpi_resp.json()["config"]["icon"] == "TrendingUp"

    filter_resp = await client.post(
        f"/dashboards/{dashboard_id}/widgets",
        headers=auth_headers,
        json={
            "type": "filter",
            "data_source": f"master:{master_id}",
            "color": "#f59e0b",
            "config": {"filter_mode": "monthly", "target_field": "placed_at", "targets": [widget_id]},
        },
    )
    assert filter_resp.status_code == 201, filter_resp.text
    assert filter_resp.json()["config"]["targets"] == [widget_id]

    list_resp = await client.get(f"/dashboards/{dashboard_id}/widgets", headers=auth_headers)
    assert len(list_resp.json()) == 3


async def test_widget_rejects_invalid_expression(client, auth_headers):
    dash_resp = await client.post("/dashboards", headers=auth_headers, json={"name": "Sales Overview"})
    dashboard_id = dash_resp.json()["id"]
    master_resp = await client.post(
        "/masters", headers=auth_headers, json={"name": "Orders2", "field_schema": [{"name": "revenue", "type": "number"}]}
    )
    master_id = master_resp.json()["id"]

    resp = await client.post(
        f"/dashboards/{dashboard_id}/widgets",
        headers=auth_headers,
        json={
            "type": "kpi_card",
            "data_source": f"master:{master_id}",
            "color": "#10b981",
            "config": {"expression": "__import__('os').system('echo pwned')"},
        },
    )
    assert resp.status_code == 422


async def test_query_series_returns_grouped_rows(client, auth_headers):
    master_resp = await client.post(
        "/masters",
        headers=auth_headers,
        json={
            "name": "Orders3",
            "field_schema": [{"name": "status", "type": "text"}, {"name": "revenue", "type": "number"}],
        },
    )
    master_id = master_resp.json()["id"]
    for status, revenue in [("open", 10), ("open", 20), ("closed", 5)]:
        await client.post(
            f"/masters/{master_id}/records", headers=auth_headers, json={"field_values": {"status": status, "revenue": revenue}}
        )

    resp = await client.post(
        "/datasets/query-series",
        headers=auth_headers,
        json={
            "source_type": "master",
            "source_id": master_id,
            "x_field": "status",
            "x_bucket": "none",
            "series": [{"field": "revenue", "aggregate": "sum", "label": "Revenue"}],
        },
    )
    assert resp.status_code == 200, resp.text
    body = resp.json()
    assert body["series"] == ["Revenue"]
    by_x = {row["x"]: row["Revenue"] for row in body["rows"]}
    assert by_x == {"open": 30, "closed": 5}


async def test_query_dataset_supports_between_and_expression(client, auth_headers):
    master_resp = await client.post(
        "/masters",
        headers=auth_headers,
        json={"name": "Orders4", "field_schema": [{"name": "revenue", "type": "number"}, {"name": "placed_at", "type": "date"}]},
    )
    master_id = master_resp.json()["id"]
    await client.post(
        f"/masters/{master_id}/records",
        headers=auth_headers,
        json={"field_values": {"revenue": 40, "placed_at": "2026-03-15"}},
    )
    await client.post(
        f"/masters/{master_id}/records",
        headers=auth_headers,
        json={"field_values": {"revenue": 100, "placed_at": "2026-05-01"}},
    )

    resp = await client.post(
        "/datasets/query",
        headers=auth_headers,
        json={
            "source_type": "master",
            "source_id": master_id,
            "filters": [{"field": "placed_at", "operator": "between", "value": ["2026-03-01", "2026-03-31"]}],
            "expression": "SUM(revenue)",
        },
    )
    assert resp.status_code == 200, resp.text
    assert resp.json()["rows"] == [{"value": 40.0}]


async def test_query_series_ignores_date_bucket_on_a_category_field(client, auth_headers):
    """A chart left with a date bucket (the designer defaults "month") while its
    X field is a category must group by that field's own values — not collapse
    every row into one "(invalid)" bar."""
    master_resp = await client.post(
        "/masters",
        headers=auth_headers,
        json={"name": "Apps By Office", "field_schema": [{"name": "office", "type": "varchar"}]},
    )
    master_id = master_resp.json()["id"]
    for office in ["Kollam", "Kollam", "Kochi"]:
        await client.post(f"/masters/{master_id}/records", headers=auth_headers, json={"field_values": {"office": office}})

    resp = await client.post(
        "/datasets/query-series",
        headers=auth_headers,
        json={
            "source_type": "master",
            "source_id": master_id,
            "x_field": "office",
            "x_bucket": "month",
            "series": [{"aggregate": "count", "label": "Applications"}],
        },
    )
    assert resp.status_code == 200, resp.text
    assert {row["x"]: row["Applications"] for row in resp.json()["rows"]} == {"Kollam": 2, "Kochi": 1}


async def test_query_series_still_buckets_real_dates(client, auth_headers):
    master_resp = await client.post(
        "/masters",
        headers=auth_headers,
        json={"name": "Dated Apps", "field_schema": [{"name": "placed_at", "type": "date"}]},
    )
    master_id = master_resp.json()["id"]
    for d in ["2026-03-01", "2026-03-20", "2026-04-02"]:
        await client.post(f"/masters/{master_id}/records", headers=auth_headers, json={"field_values": {"placed_at": d}})

    resp = await client.post(
        "/datasets/query-series",
        headers=auth_headers,
        json={
            "source_type": "master",
            "source_id": master_id,
            "x_field": "placed_at",
            "x_bucket": "month",
            "series": [{"aggregate": "count", "label": "Count"}],
        },
    )
    assert resp.status_code == 200, resp.text
    assert [(r["x"], r["Count"]) for r in resp.json()["rows"]] == [("2026-03", 2), ("2026-04", 1)]
