import asyncio

import pytest
from fastapi.testclient import TestClient

from app.repositories import embedding_run_repo
from app.services.pipeline_service import run_snapshot_embedding_pipeline
from tests.integration.fakes import seed_chunked_snapshot_graph, seed_snapshot_graph, seed_snapshot_upstream


@pytest.fixture
def api_client(monkeypatch):
    monkeypatch.setattr("app.main.bootstrap_qdrant", lambda *_args, **_kwargs: "adpilot_embeddings")
    from app.main import app

    return TestClient(app)


def _index_demo_snapshot(integration_env) -> None:
    fake_db = integration_env["db"]
    seed_snapshot_upstream(fake_db)

    run = asyncio.run(
        embedding_run_repo.upsert_run(
            repo_id="AD/example-repo",
            snapshot_id="snap_abc123",
            event_id="evt_retrieve_integration",
            run_type="snapshot_batch",
        )
    )
    result = asyncio.run(
        run_snapshot_embedding_pipeline(
            run_id=run.run_id,
            repo_id="AD/example-repo",
            snapshot_id="snap_abc123",
            commit_sha="a1b2c3d4e5f6",
            artifact_uri="mongo://snapshot_graphs/art_snap_abc123_merged",
            event_id="evt_retrieve_integration",
        )
    )
    assert result["status"] == "completed"
    assert result["embed"]["indexed"] == 3


@pytest.mark.integration
def test_internal_retrieve_returns_hydrated_code_and_docs(integration_env, api_client):
    _index_demo_snapshot(integration_env)

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "How does configuration loading work?",
            "request_id": "req_integration_1",
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["embedding_model"] == "text-embedding-3-small"
    assert body["embedding_dimension"] == 1536
    assert body["metadata"]["request_id"] == "req_integration_1"
    assert body["metadata"]["retrieval_count"] >= 1
    assert body["metadata"]["hydrated_count"] >= 1
    assert body["metadata"]["latency_ms"] >= 0
    assert body["metadata"]["vector_latency_ms"] >= 0
    assert body["metadata"]["hydrate_latency_ms"] >= 0
    assert body["metadata"]["graph_latency_ms"] == 0
    assert len(body["hits"]) >= 1
    assert all(hit["text"] for hit in body["hits"])
    assert len(body["code_snippets"]) + len(body["doc_excerpts"]) >= 1


@pytest.mark.integration
def test_internal_retrieve_filters_source_types(integration_env, api_client):
    _index_demo_snapshot(integration_env)

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "configuration",
            "filters": {"source_types": ["code"]},
            "request_id": "req_integration_filter",
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["doc_excerpts"] == []
    assert len(body["code_snippets"]) >= 1
    assert all(hit["source_type"] == "code" for hit in body["hits"])


@pytest.mark.integration
def test_internal_retrieve_graph_expand_adds_linked_doc(integration_env, api_client):
    _index_demo_snapshot(integration_env)
    seed_snapshot_graph(integration_env["db"])

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "configuration",
            "filters": {"source_types": ["code"]},
            "graph_expand": True,
            "request_id": "req_graph_expand",
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["metadata"]["graph_nodes_expanded"] >= 1
    assert body["metadata"]["graph_records_added"] >= 1
    assert any(item.get("graph_expanded") for item in body["doc_excerpts"])
    assert body["metadata"]["graph_latency_ms"] >= 0


@pytest.mark.integration
def test_internal_retrieve_graph_expand_works_with_chunked_snapshot_graph(integration_env, api_client):
    _index_demo_snapshot(integration_env)
    seed_chunked_snapshot_graph(integration_env["db"])

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "configuration",
            "filters": {"source_types": ["code"]},
            "graph_expand": True,
            "request_id": "req_graph_expand_chunked",
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["metadata"]["graph_nodes_expanded"] >= 1
    assert body["metadata"]["graph_records_added"] >= 1
    assert any(item.get("graph_expanded") for item in body["doc_excerpts"])


@pytest.mark.integration
def test_internal_retrieve_graph_expand_disabled_skips_traversal(integration_env, api_client):
    _index_demo_snapshot(integration_env)
    seed_snapshot_graph(integration_env["db"])

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "configuration",
            "filters": {"source_types": ["code"]},
            "graph_expand": False,
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["metadata"]["graph_nodes_expanded"] == 0
    assert body["metadata"]["graph_records_added"] == 0


@pytest.mark.integration
def test_internal_retrieve_unknown_snapshot_returns_404(integration_env, api_client):
    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_never_indexed",
            "query_text": "hello",
            "request_id": "req_missing_snap",
        },
    )

    assert response.status_code == 404
    body = response.json()
    assert body["error"]["code"] == "snapshot_not_found"
    assert body["error"]["request_id"] == "req_missing_snap"


@pytest.mark.integration
def test_internal_retrieve_high_score_threshold_returns_empty_arrays(integration_env, api_client):
    _index_demo_snapshot(integration_env)

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "configuration",
            "score_threshold": 0.99,
            "request_id": "req_empty_hits",
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["hits"] == []
    assert body["code_snippets"] == []
    assert body["doc_excerpts"] == []
    assert body["related_commits"] == []
    assert body["metadata"]["retrieval_count"] == 0
    assert body["metadata"]["hydrated_count"] == 0


@pytest.mark.integration
def test_internal_retrieve_rejects_empty_query(integration_env, api_client):
    _index_demo_snapshot(integration_env)

    response = api_client.post(
        "/internal/retrieve",
        json={
            "repo_id": "AD/example-repo",
            "snapshot_id": "snap_abc123",
            "query_text": "   ",
        },
    )

    assert response.status_code == 422
    assert response.json()["error"]["code"] == "validation_error"
