import pytest

from metaverse.tools.metrics import (
    archive_metric,
    create_metric,
    execute_metric,
    get_metric,
    list_metrics,
)
from tests.conftest import json_response

_METRIC_CARD = {
    "id": 1,
    "name": "Total Revenue",
    "description": "Sum of all revenue",
    "type": "metric",
    "table_id": 5,
    "database_id": 2,
    "archived": False,
    "dataset_query": {
        "database": 2,
        "type": "query",
        "query": {
            "source-table": 5,
            "aggregation": [["sum", ["field", 42, None]]],
        },
    },
    "display": "scalar",
    "visualization_settings": {},
}


async def test_list_metrics(mock_api):
    question_card = {**_METRIC_CARD, "id": 2, "type": "question", "name": "A Question"}
    mock_api.get("/card").mock(return_value=json_response([_METRIC_CARD, question_card]))
    result = await list_metrics()
    assert len(result) == 1
    assert result[0]["id"] == 1
    assert result[0]["name"] == "Total Revenue"
    assert result[0]["table_id"] == 5
    assert result[0]["database_id"] == 2
    assert result[0]["archived"] is False


async def test_list_metrics_empty(mock_api):
    mock_api.get("/card").mock(return_value=json_response([]))
    result = await list_metrics()
    assert result == []


async def test_get_metric(mock_api):
    mock_api.get("/card/1").mock(return_value=json_response(_METRIC_CARD))
    result = await get_metric(1)
    assert result["id"] == 1
    assert result["name"] == "Total Revenue"
    assert result["description"] == "Sum of all revenue"
    assert result["table_id"] == 5
    assert result["database_id"] == 2
    assert result["archived"] is False
    assert result["definition"]["source-table"] == 5
    assert result["definition"]["aggregation"] == [["sum", ["field", 42, None]]]


async def test_create_metric_sum(mock_api):
    mock_api.post("/card").mock(return_value=json_response(_METRIC_CARD))
    result = await create_metric(
        name="Total Revenue",
        database_id=2,
        table_id=5,
        aggregation_type="sum",
        field_id=42,
        description="Sum of all revenue",
    )
    assert result["id"] == 1
    assert result["name"] == "Total Revenue"
    assert "metric/1" in result["url"] or "/1" in result["url"]

    request = mock_api.calls[0].request
    import json

    body = json.loads(request.content)
    assert body["type"] == "metric"
    assert body["dataset_query"]["query"]["source-table"] == 5
    assert body["dataset_query"]["query"]["aggregation"] == [["sum", ["field", 42, None]]]


async def test_create_metric_count(mock_api):
    count_card = {**_METRIC_CARD, "name": "Order Count"}
    mock_api.post("/card").mock(return_value=json_response(count_card))
    result = await create_metric(
        name="Order Count",
        database_id=2,
        table_id=5,
        aggregation_type="count",
    )
    assert result["name"] == "Order Count"

    request = mock_api.calls[0].request
    import json

    body = json.loads(request.content)
    assert body["dataset_query"]["query"]["aggregation"] == [["count"]]


async def test_create_metric_missing_field_id():
    with pytest.raises(ValueError, match="field_id is required"):
        await create_metric(
            name="Avg Price",
            database_id=2,
            table_id=5,
            aggregation_type="avg",
            field_id=None,
        )


async def test_archive_metric(mock_api):
    mock_api.get("/card/1").mock(return_value=json_response(_METRIC_CARD))
    mock_api.put("/card/1").mock(return_value=json_response({**_METRIC_CARD, "archived": True}))
    result = await archive_metric(1)
    assert result == {"archived": True, "metric_id": 1}

    request = mock_api.calls[1].request
    import json

    body = json.loads(request.content)
    assert body["archived"] is True


async def test_execute_metric_list_response(mock_api):
    mock_api.post("/card/1/query/json").mock(
        return_value=json_response([{"Total Revenue": 12345.67}])
    )
    result = await execute_metric(1)
    assert result["columns"] == ["Total Revenue"]
    assert result["rows"] == [[12345.67]]
    assert result["row_count"] == 1
    assert result["truncated"] is False


async def test_execute_metric_dict_response(mock_api):
    mock_api.post("/card/1/query/json").mock(
        return_value=json_response({"data": {"cols": [{"name": "count"}], "rows": [[42]]}})
    )
    result = await execute_metric(1)
    assert result["columns"] == ["count"]
    assert result["rows"] == [[42]]
    assert result["row_count"] == 1
