"""Integration tests for traces — full lifecycle without an LLM."""

from __future__ import annotations


def _create_project(client, name="Trace Project"):
    resp = client.post("/api/v1/projects", json={"name": name})
    assert resp.status_code == 201
    return resp.json()


def _make_trace(question="What is Python?", answer="A programming language.", expected_answer="Python", hour=10):
    return {
        "test_case": {
            "question": question,
            "answer": answer,
            "expected_answer": expected_answer,
        },
        "timestamp": f"2026-03-24T{hour:02d}:00:00Z",
        "metadata": {"model": "gpt-4o", "env": "prod"},
    }


def _push_traces(client, project_id, count=5, base_hour=10, suite="answer_similarity"):
    traces = [
        _make_trace(
            question=f"Question {i}",
            answer=f"Answer {i}",
            expected_answer=f"Answer {i}" if i % 2 == 0 else "different",
            hour=base_hour + i,
        )
        for i in range(count)
    ]
    resp = client.post(
        "/api/v1/traces",
        json={"project_id": project_id, "suite": suite, "traces": traces},
    )
    return resp


class TestTraceIngest:
    def test_push_single_trace(self, client):
        project = _create_project(client, "ingest-single")
        resp = client.post(
            "/api/v1/traces",
            json={
                "project_id": project["id"],
                "suite": "answer_similarity",
                "traces": [_make_trace()],
            },
        )
        assert resp.status_code == 201
        data = resp.json()
        assert data["count"] == 1
        assert len(data["trace_ids"]) == 1

    def test_push_batch(self, client):
        project = _create_project(client, "ingest-batch")
        resp = _push_traces(client, project["id"], count=5)
        assert resp.status_code == 201
        assert resp.json()["count"] == 5

    def test_push_with_metadata(self, client):
        project = _create_project(client, "ingest-meta")
        trace = _make_trace()
        trace["metadata"] = {"model": "gpt-4o", "user_id": "123"}
        resp = client.post(
            "/api/v1/traces",
            json={"project_id": project["id"], "traces": [trace]},
        )
        assert resp.status_code == 201
        trace_id = resp.json()["trace_ids"][0]

        detail = client.get(f"/api/v1/traces/{trace_id}")
        assert detail.json()["metadata"]["model"] == "gpt-4o"
        assert detail.json()["metadata"]["user_id"] == "123"

    def test_push_without_timestamp_uses_server_time(self, client):
        project = _create_project(client, "ingest-no-ts")
        trace = {"test_case": {"question": "Q", "answer": "A"}}
        resp = client.post(
            "/api/v1/traces",
            json={"project_id": project["id"], "traces": [trace]},
        )
        assert resp.status_code == 201
        trace_id = resp.json()["trace_ids"][0]
        detail = client.get(f"/api/v1/traces/{trace_id}")
        assert detail.json()["timestamp"] is not None

    def test_push_to_nonexistent_project_404(self, client):
        resp = client.post(
            "/api/v1/traces",
            json={"project_id": 9999, "traces": [_make_trace()]},
        )
        assert resp.status_code == 404

    def test_trace_stores_test_case(self, client):
        project = _create_project(client, "ingest-tc")
        trace = _make_trace(question="What is FastAPI?", answer="A web framework.")
        resp = client.post(
            "/api/v1/traces",
            json={"project_id": project["id"], "traces": [trace]},
        )
        trace_id = resp.json()["trace_ids"][0]
        detail = client.get(f"/api/v1/traces/{trace_id}")
        data = detail.json()
        assert data["test_case"]["question"] == "What is FastAPI?"
        assert data["test_case"]["answer"] == "A web framework."
        assert data["question"] == "What is FastAPI?"

    def test_server_evaluates_traces_with_suite(self, client):
        project = _create_project(client, "ingest-eval")
        trace = _make_trace(answer="Python", expected_answer="Python")
        resp = client.post(
            "/api/v1/traces",
            json={"project_id": project["id"], "suite": "answer_similarity", "traces": [trace]},
        )
        trace_id = resp.json()["trace_ids"][0]
        detail = client.get(f"/api/v1/traces/{trace_id}")
        scores = detail.json()["scores"]
        assert len(scores) > 0
        metric_names = {s["metric_name"] for s in scores}
        assert "exact_match" in metric_names


class TestTraceQuery:
    def test_list_traces(self, client):
        project = _create_project(client, "query-list")
        _push_traces(client, project["id"], count=3)

        resp = client.get(f"/api/v1/traces?project_id={project['id']}")
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 3
        assert len(data["traces"]) == 3

    def test_list_with_pagination(self, client):
        project = _create_project(client, "query-page")
        _push_traces(client, project["id"], count=5)

        resp = client.get(f"/api/v1/traces?project_id={project['id']}&limit=2&offset=0")
        data = resp.json()
        assert len(data["traces"]) == 2
        assert data["total"] == 5

    def test_list_with_date_filter(self, client):
        project = _create_project(client, "query-date")
        _push_traces(client, project["id"], count=5, base_hour=10)

        resp = client.get(
            f"/api/v1/traces?project_id={project['id']}" "&start=2026-03-24T12:00:00Z&end=2026-03-24T14:00:00Z"
        )
        data = resp.json()
        assert data["total"] == 3

    def test_get_single_trace(self, client):
        project = _create_project(client, "query-single")
        resp = _push_traces(client, project["id"], count=1)
        trace_id = resp.json()["trace_ids"][0]

        detail = client.get(f"/api/v1/traces/{trace_id}")
        assert detail.status_code == 200
        data = detail.json()
        assert data["id"] == trace_id
        assert data["test_case"]["question"] == "Question 0"

    def test_get_nonexistent_trace_404(self, client):
        resp = client.get("/api/v1/traces/99999")
        assert resp.status_code == 404

    def test_list_traces_nonexistent_project_404(self, client):
        resp = client.get("/api/v1/traces?project_id=99999")
        assert resp.status_code == 404


class TestMonitorEndpoint:
    def test_monitor_returns_buckets(self, client):
        project = _create_project(client, "monitor-buckets")
        _push_traces(client, project["id"], count=5, base_hour=10)

        resp = client.get(f"/api/v1/projects/{project['id']}/monitor?interval=1h")
        assert resp.status_code == 200
        data = resp.json()
        assert len(data["buckets"]) > 0
        assert "avg_score" in data["buckets"][0]
        assert "summary" in data

    def test_monitor_summary(self, client):
        project = _create_project(client, "monitor-summary")
        _push_traces(client, project["id"], count=3)

        resp = client.get(f"/api/v1/projects/{project['id']}/monitor")
        data = resp.json()
        assert data["summary"]["total_traces"] == 3

    def test_monitor_nonexistent_project_404(self, client):
        resp = client.get("/api/v1/projects/99999/monitor")
        assert resp.status_code == 404


class TestDashboardPages:
    def test_monitor_page_renders(self, client):
        project = _create_project(client, "html-monitor")
        _push_traces(client, project["id"], count=3)

        resp = client.get(f"/projects/{project['id']}/monitor")
        assert resp.status_code == 200
        assert "Traces" in resp.text

    def test_monitor_page_empty(self, client):
        project = _create_project(client, "html-empty-monitor")
        resp = client.get(f"/projects/{project['id']}/monitor")
        assert resp.status_code == 200
        assert "No traces yet" in resp.text

    def test_trace_detail_page_renders(self, client):
        project = _create_project(client, "html-trace-detail")
        resp = _push_traces(client, project["id"], count=1)
        trace_id = resp.json()["trace_ids"][0]

        page = client.get(f"/traces/{trace_id}")
        assert page.status_code == 200
        assert "Question 0" in page.text

    def test_project_page_has_traces_tab(self, client):
        project = _create_project(client, "html-traces-tab")
        resp = client.get(f"/projects/{project['id']}")
        assert resp.status_code == 200
        assert "Traces" in resp.text
        assert f"/projects/{project['id']}/monitor" in resp.text
