from __future__ import annotations

from amplitude_ai.mcp.server import (
    _get_event_schema,
    _get_integration_pattern,
    _suggest_instrumentation,
    _validate_file,
    _validate_setup,
)


def test_get_event_schema_has_events() -> None:
    result = _get_event_schema()
    assert result["total"] > 0
    assert isinstance(result["events"], list)


def test_get_integration_pattern_returns_known_patterns() -> None:
    result = _get_integration_pattern()
    assert isinstance(result["patterns"], list)
    assert len(result["patterns"]) > 0


def test_validate_setup_has_status() -> None:
    result = _validate_setup()
    assert result["status"] in {"ok", "missing_env"}
    assert isinstance(result["missing_env"], list)


def test_suggest_instrumentation_contains_steps() -> None:
    result = _suggest_instrumentation(framework="fastapi", provider="openai")
    assert result["framework"] == "fastapi"
    assert result["provider"] == "openai"
    assert result["content_tier"] == "full"
    assert len(result["steps"]) >= 8
    joined = " ".join(result["steps"])
    assert "Now:" in joined
    assert "Next:" in joined
    assert "Why:" in joined


def test_validate_file_detects_uninstrumented_openai() -> None:
    source = '''
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(model="gpt-4", messages=[])
'''
    result = _validate_file(source=source)
    assert result["total_call_sites"] >= 1
    assert result["uninstrumented"] >= 1
    assert result["has_amplitude_import"] is False
    assert len(result["suggestions"]) > 0


def test_validate_file_detects_instrumented_code() -> None:
    source = '''
from amplitude_ai import AmplitudeAI, OpenAI
ai = AmplitudeAI(amplitude=amp)
client = OpenAI(amplitude=ai)
response = client.chat.completions.create(model="gpt-4", messages=[])
'''
    result = _validate_file(source=source)
    assert result["total_call_sites"] >= 1
    assert result["has_amplitude_import"] is True
    assert result["uninstrumented"] == 0
    assert result["has_session_context"] is False
    assert "session context is missing" in " ".join(result["suggestions"])


def test_validate_file_ast_finds_nested_call_chain() -> None:
    source = '''
def handle_request(client):
    result = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "hi"}],
    )
    return result
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["call_sites"][0]["provider"] == "openai"
    assert result["call_sites"][0]["api"] == "chat.completions.create"


def test_validate_file_ast_finds_anthropic_messages() -> None:
    source = '''
response = anthropic_client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": "hello"}],
)
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["call_sites"][0]["provider"] == "anthropic"


def test_validate_file_falls_back_to_regex_for_non_python() -> None:
    source = 'const r = await client.chat.completions.create({ model: "gpt-4" });'
    result = _validate_file(source=source, language="javascript")
    assert result["total_call_sites"] >= 1
    assert result["call_sites"][0]["provider"] == "openai"


def test_suggest_instrumentation_fastapi_specific() -> None:
    result = _suggest_instrumentation(
        framework="fastapi",
        provider="anthropic",
        content_tier="metadata_only",
    )
    joined = " ".join(result["steps"])
    assert "AmplitudeAIMiddleware" in joined
    assert "anthropic" in joined
    assert "metadata_only" in joined


def test_suggest_instrumentation_generic_framework() -> None:
    result = _suggest_instrumentation(framework="cli", provider="gemini")
    joined = " ".join(result["steps"])
    assert "BoundAgent" in joined
    assert "gemini" in joined
    assert "migration quickstart" in joined


def test_validate_file_mixed_instrumented_and_raw() -> None:
    """File with both a wrapped client and a raw client should classify correctly."""
    source = '''
from amplitude_ai import AmplitudeAI, OpenAI
ai = AmplitudeAI(amplitude=amp)
wrapped = OpenAI(amplitude=ai, api_key="sk-...")
raw_client = __import__("openai").OpenAI(api_key="sk-...")

# This should be instrumented (wrapped client)
response1 = wrapped.chat.completions.create(model="gpt-4o", messages=[])

# This should NOT be instrumented (raw client)
response2 = raw_client.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 2
    assert result["has_amplitude_import"] is True

    sites = result["call_sites"]
    wrapped_site = next(s for s in sites if s["line"] == 8)
    raw_site = next(s for s in sites if s["line"] == 11)

    assert wrapped_site["instrumented"] is True
    assert raw_site["instrumented"] is False
    assert result["instrumented"] == 1
    assert result["uninstrumented"] == 1


def test_validate_file_patch_marks_all_instrumented() -> None:
    """When patch() is present, all call sites should be marked instrumented."""
    source = '''
from amplitude_ai import patch
patch(amplitude=amp)

response1 = client1.chat.completions.create(model="gpt-4o", messages=[])
response2 = client2.messages.create(model="claude-3", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 2
    assert result["uninstrumented"] == 0
    assert result["has_session_context"] is False
    for site in result["call_sites"]:
        assert site["instrumented"] is True


def test_validate_file_marks_session_context_when_present() -> None:
    source = '''
from amplitude_ai import AmplitudeAI, OpenAI
ai = AmplitudeAI(amplitude=amp)
client = OpenAI(amplitude=ai, api_key="sk-...")
agent = ai.agent("assistant", user_id="u1")
with agent.session(session_id="s1"):
    response = client.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["has_session_context"] is True
    assert "session lineage" in " ".join(result["suggestions"])


def test_validate_file_wrap_detected_as_instrumented() -> None:
    """wrap() with amplitude= kwarg should be recognized as instrumented."""
    source = '''
from amplitude_ai import AmplitudeAI, wrap
import openai

ai = AmplitudeAI(amplitude=amp)
raw = openai.OpenAI(api_key="sk-...")
w = wrap(raw, amplitude=ai)

response = w.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["instrumented"] == 1
    assert result["uninstrumented"] == 0
    assert result["call_sites"][0]["instrumented"] is True


def test_validate_file_non_amplitude_wrap_not_detected() -> None:
    """Non-amplitude wrap() calls (e.g. textwrap.wrap) should not be detected."""
    source = '''
import textwrap
import openai

client = openai.OpenAI(api_key="sk-...")
lines = textwrap.wrap("some long text", width=80)
w = textwrap.wrap("more text", width=80)

response = client.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["instrumented"] == 0
    assert result["uninstrumented"] == 1


def test_validate_file_multiline_wrapped_constructor() -> None:
    """Multiline constructor calls with amplitude kwarg should be detected."""
    source = '''
from amplitude_ai import AmplitudeAI, OpenAI
ai = AmplitudeAI(amplitude=amp)
client = OpenAI(
    api_key="sk-...",
    amplitude=ai,
    timeout=30,
)

response = client.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["instrumented"] == 1
    assert result["uninstrumented"] == 0
    assert result["call_sites"][0]["instrumented"] is True


def test_validate_file_nested_dict_before_amplitude_kwarg() -> None:
    """Constructor with nested dict before amplitude kwarg should be detected."""
    source = '''
from amplitude_ai import AmplitudeAI, OpenAI
ai = AmplitudeAI(amplitude=amp)
client = OpenAI(
    config={"timeout": 5000, "retry": True},
    amplitude=ai,
    api_key="sk-...",
)

response = client.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["instrumented"] == 1
    assert result["uninstrumented"] == 0
    assert result["call_sites"][0]["instrumented"] is True


def test_validate_file_module_qualified_wrap_detected() -> None:
    """amplitude_ai.wrap() (module-qualified) should also be recognized."""
    source = '''
import amplitude_ai
import openai

amp = amplitude_ai.AmplitudeAI(amplitude=amplitude)
raw = openai.OpenAI(api_key="sk-...")
wrapped = amplitude_ai.wrap(raw, amplitude=amp, user_id="u1")

response = wrapped.chat.completions.create(model="gpt-4o", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["instrumented"] == 1
    assert result["uninstrumented"] == 0
    assert result["call_sites"][0]["instrumented"] is True


def test_validate_file_nested_parens_regex_fallback() -> None:
    """Regex fallback must handle nested parens like OpenAI(config=Config(), amplitude=ai)."""
    source = 'client = OpenAI(config=Config(), amplitude=ai)\nresponse = client.chat.completions.create(model="gpt-4o", messages=[])'
    result = _validate_file(source=source, language="javascript")
    assert result["total_call_sites"] == 1
    assert result["instrumented"] == 1, "Nested parens before amplitude= should not break detection"


def test_validate_file_ignores_unrelated_single_method_calls_without_provider_signal() -> None:
    source = '''
class ConversationManager:
    def converse(self) -> str:
        return "ok"

manager = ConversationManager()
manager.converse()
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 0
    assert result["call_sites"] == []


def test_validate_file_detects_bedrock_single_method_with_provider_signal() -> None:
    source = '''
import boto3
client = boto3.client("bedrock-runtime")
response = client.converse(modelId="m", messages=[])
'''
    result = _validate_file(source=source, language="python")
    assert result["total_call_sites"] == 1
    assert result["call_sites"][0]["provider"] == "bedrock"
    assert result["call_sites"][0]["api"] == "converse"
