import pytest

from app.intent.intent_analyzer import IntentAnalyzer
from app.models.internal_retrieve import (
    CodeSnippet,
    DocExcerpt,
    RelatedCommit,
    RetrieveResponse,
)
from app.retrieval.context_builder import ContextBuilder


def _response_with_duplicates() -> RetrieveResponse:
    return RetrieveResponse(
        embedding_model="text-embedding-3-small",
        embedding_dimension=1536,
        code_snippets=[
            CodeSnippet(
                record_id="emb_a",
                file_path="a.go",
                symbol_name="Foo",
                text="alpha " * 50,
                score=0.7,
            ),
            CodeSnippet(
                record_id="emb_a",
                file_path="a.go",
                symbol_name="Foo",
                text="alpha duplicate",
                score=0.9,
            ),
            CodeSnippet(
                record_id="emb_b",
                file_path="b.go",
                symbol_name="Bar",
                text="beta",
                score=0.8,
            ),
        ],
        doc_excerpts=[
            DocExcerpt(
                record_id="emb_doc",
                doc_path="README.md",
                section_title="Setup",
                text="setup docs",
                score=0.75,
            )
        ],
        related_commits=[
            RelatedCommit(
                record_id="emb_commit",
                commit_sha="abc123",
                summary="refactor parser",
                score=0.6,
            )
        ],
    )


def test_dedupe_keeps_highest_score():
    intent = IntentAnalyzer().analyze("where is Foo defined")
    built = ContextBuilder(token_budget=100000).build(_response_with_duplicates(), intent)
    record_ids = [c.record_id for c in built.chunks]
    assert record_ids.count("emb_a") == 1
    assert "emb_b" in record_ids


def test_rank_prefers_intent_source_types():
    intent = IntentAnalyzer().analyze("where is Foo defined")
    built = ContextBuilder(token_budget=100000).build(_response_with_duplicates(), intent)
    assert built.chunks[0].source_type.value == "code"


def test_token_budget_trims_context():
    intent = IntentAnalyzer().analyze("explain system")
    huge = RetrieveResponse(
        embedding_model="text-embedding-3-small",
        embedding_dimension=1536,
        code_snippets=[
            CodeSnippet(
                record_id=f"emb_{i}",
                file_path=f"f{i}.go",
                symbol_name=f"S{i}",
                text="x " * 5000,
                score=1.0 - i * 0.01,
            )
            for i in range(10)
        ],
    )
    built = ContextBuilder(token_budget=500).build(huge, intent)
    assert built.total_tokens <= 500
    assert built.trimmed_count > 0
    assert len(built.chunks) < 10


def test_commit_context_includes_changed_files():
    intent = IntentAnalyzer().analyze("latest commit changes")
    response = RetrieveResponse(
        embedding_model="text-embedding-3-small",
        embedding_dimension=1536,
        related_commits=[
            RelatedCommit(
                record_id="emb_commit",
                commit_sha="abc123",
                summary="5 files changed",
                changed_files=["internal/app/app.go", "internal/config/config.go"],
                score=0.6,
            )
        ],
    )
    built = ContextBuilder(token_budget=100000).build(response, intent)
    assert len(built.chunks) == 1
    assert "Summary: 5 files changed" in built.chunks[0].text
    assert "internal/app/app.go" in built.chunks[0].text
    assert "internal/config/config.go" in built.chunks[0].text


def test_commit_context_includes_author_metadata():
    intent = IntentAnalyzer().analyze("who authored the latest commit")
    response = RetrieveResponse(
        embedding_model="text-embedding-3-small",
        embedding_dimension=1536,
        related_commits=[
            RelatedCommit(
                record_id="emb_commit",
                commit_sha="abc123",
                summary="Refactor parser",
                author_name="Alice",
                author_email="alice@example.com",
                authored_at="2026-06-01T10:00:00+00:00",
                commit_message="Refactor parser\n\nDetails here.",
                score=0.6,
            )
        ],
    )
    built = ContextBuilder(token_budget=100000).build(response, intent)
    assert len(built.chunks) == 1
    text = built.chunks[0].text
    assert "Author: Alice <alice@example.com>" in text
    assert "2026-06-01T10:00:00+00:00" in text
    assert "Refactor parser" in text
    assert "Details here." in text


def test_citation_mapper_returns_metadata():
    intent = IntentAnalyzer().analyze("where is Foo defined")
    built = ContextBuilder(token_budget=100000).build(_response_with_duplicates(), intent)
    citations = ContextBuilder().map_citations([c.index for c in built.chunks[:2]], built)
    assert len(citations) == 2
    assert citations[0].record_id == built.chunks[0].record_id
    assert citations[0].file_path or citations[0].doc_path or citations[0].commit_sha


def test_citation_mapper_drops_hallucinated_indices():
    intent = IntentAnalyzer().analyze("where is Foo defined")
    built = ContextBuilder(token_budget=100000).build(_response_with_duplicates(), intent)
    valid = built.chunks[0].index
    citations = ContextBuilder().map_citations([valid, 999, -1], built)
    assert len(citations) == 1
    assert citations[0].index == valid
