"""Integration tests for PCH (precompiled header) functionality in MasterServer."""

import asyncio

import pytest
from lsprotocol.types import (
    CompletionParams,
    DidChangeTextDocumentParams,
    DidCloseTextDocumentParams,
    HoverParams,
    Position,
    TextDocumentContentChangeWholeDocument,
    TextDocumentIdentifier,
    VersionedTextDocumentIdentifier,
)


def _doc(uri: str) -> TextDocumentIdentifier:
    return TextDocumentIdentifier(uri=uri)


@pytest.mark.workspace("pch_test")
async def test_pch_diagnostics_on_open(client, workspace):
    """Opening a file with #include should trigger PCH build and return clean diagnostics."""
    uri, _ = await client.open_and_wait(workspace / "main.cpp")
    assert uri in client.diagnostics
    # main.cpp is well-formed, so diagnostics list should be empty (no errors).
    diags = client.diagnostics[uri]
    assert len(diags) == 0, f"Expected no diagnostics, got: {diags}"
    client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))


@pytest.mark.workspace("pch_test")
async def test_pch_body_edit_triggers_recompile(client, workspace):
    """Editing only the body (not the preamble) should trigger recompilation."""
    uri, content = await client.open_and_wait(workspace / "main.cpp")

    # Edit only the function body — preamble (#include "common.h") unchanged.
    new_content = content.replace("return result;", "return result + 1;")
    event = client.wait_for_diagnostics(uri)
    client.text_document_did_change(
        DidChangeTextDocumentParams(
            text_document=VersionedTextDocumentIdentifier(uri=uri, version=1),
            content_changes=[TextDocumentContentChangeWholeDocument(text=new_content)],
        )
    )
    # The key assertion: recompilation completes (diagnostics event fires).
    await asyncio.wait_for(event.wait(), timeout=30.0)
    assert uri in client.diagnostics
    client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))


@pytest.mark.workspace("pch_test")
async def test_no_pch_for_no_includes(client, workspace):
    """A file with no #include directives should compile without PCH."""
    uri, _ = await client.open_and_wait(workspace / "no_includes.cpp")
    assert uri in client.diagnostics
    diags = client.diagnostics[uri]
    assert len(diags) == 0, f"Expected no diagnostics, got: {diags}"
    client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))


@pytest.mark.workspace("pch_test")
async def test_hover_on_local_symbol(client, workspace):
    """Hover on a locally defined symbol should work when PCH is active."""
    uri, _ = await client.open_and_wait(workspace / "main.cpp")

    # Hover over "add" on line 2 (0-indexed): "int add(int a, int b) {"
    result = await client.text_document_hover_async(
        HoverParams(text_document=_doc(uri), position=Position(line=2, character=4))
    )
    assert result is not None
    client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))


@pytest.mark.workspace("pch_test")
async def test_completion_with_pch(client, workspace):
    """Completion should see symbols from PCH headers."""
    uri, content = await client.open_and_wait(workspace / "main.cpp")

    # Add a line that starts typing "Poi" to trigger completion for Point.
    new_content = content + "\nPoi"
    lines = new_content.split("\n")
    last_line = len(lines) - 1

    event = client.wait_for_diagnostics(uri)
    client.text_document_did_change(
        DidChangeTextDocumentParams(
            text_document=VersionedTextDocumentIdentifier(uri=uri, version=1),
            content_changes=[TextDocumentContentChangeWholeDocument(text=new_content)],
        )
    )
    # Brief wait for the change to be processed.
    await asyncio.sleep(1.0)

    result = await client.text_document_completion_async(
        CompletionParams(
            text_document=_doc(uri),
            position=Position(line=last_line, character=3),
        )
    )
    # Completion should return results.
    assert result is not None
    client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))
