from typing import Any

import pytest

from free_claude_code.core.openai_responses import (
    OpenAIResponsesAdapter,
    OpenAIResponsesRequest,
)

_ADAPTER = OpenAIResponsesAdapter()
_CONVERSION_ERROR = OpenAIResponsesAdapter.ConversionError


def _to_anthropic_payload(request: dict[str, Any]) -> dict[str, Any]:
    return _ADAPTER.to_anthropic_payload(OpenAIResponsesRequest.model_validate(request))


def test_responses_string_input_converts_to_anthropic_message() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "instructions": "System instructions",
            "input": "Hello",
            "max_output_tokens": 64,
            "temperature": 0.2,
            "top_p": 0.9,
            "metadata": {"trace": "abc"},
        }
    )

    assert payload["model"] == "nvidia_nim/test-model"
    assert payload["system"] == "System instructions"
    assert payload["messages"] == [{"role": "user", "content": "Hello"}]
    assert payload["max_tokens"] == 64
    assert payload["temperature"] == 0.2
    assert payload["top_p"] == 0.9
    assert payload["metadata"] == {"trace": "abc"}


@pytest.mark.parametrize("effort", ["none", "low", "medium", "high", "xhigh"])
def test_responses_reasoning_effort_is_preserved_for_application_policy(
    effort: str,
) -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": "Hello",
            "reasoning": {"effort": effort},
        }
    )

    assert payload["output_config"] == {"effort": effort}
    assert "thinking" not in payload


def test_responses_messages_tools_and_tool_results_convert() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "deepseek/deepseek-chat",
            "input": [
                {
                    "type": "message",
                    "role": "developer",
                    "content": [{"type": "input_text", "text": "Developer rules"}],
                },
                {
                    "type": "message",
                    "role": "user",
                    "content": [{"type": "input_text", "text": "Use the tool"}],
                },
                {
                    "type": "function_call",
                    "call_id": "call_1",
                    "name": "echo",
                    "arguments": '{"value":"FCC"}',
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_1",
                    "output": "FCC",
                },
            ],
            "tools": [
                {
                    "type": "function",
                    "name": "echo",
                    "description": "Echo a value",
                    "parameters": {
                        "type": "object",
                        "properties": {"value": {"type": "string"}},
                    },
                }
            ],
            "tool_choice": {"type": "function", "name": "echo"},
        }
    )

    assert payload["system"] == "Developer rules"
    assert payload["messages"] == [
        {"role": "user", "content": [{"type": "text", "text": "Use the tool"}]},
        {
            "role": "assistant",
            "content": [
                {
                    "type": "tool_use",
                    "id": "call_1",
                    "name": "echo",
                    "input": {"value": "FCC"},
                }
            ],
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "call_1",
                    "content": "FCC",
                }
            ],
        },
    ]
    assert payload["tools"] == [
        {
            "name": "echo",
            "description": "Echo a value",
            "input_schema": {
                "type": "object",
                "properties": {"value": {"type": "string"}},
            },
        }
    ]
    assert payload["tool_choice"] == {"type": "tool", "name": "echo"}


def test_responses_tool_choice_none_disables_forwarded_tools() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "deepseek/deepseek-chat",
            "input": "Reply without tools",
            "tools": [
                {
                    "type": "function",
                    "name": "echo",
                    "parameters": {
                        "type": "object",
                        "properties": {"value": {"type": "string"}},
                    },
                }
            ],
            "tool_choice": "none",
        }
    )

    assert "tools" not in payload
    assert "tool_choice" not in payload


def test_responses_namespace_tools_flatten_for_anthropic() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": "Use JS",
            "tools": [
                {
                    "type": "namespace",
                    "name": "mcp__node_repl",
                    "description": "Node tools",
                    "tools": [
                        {
                            "type": "function",
                            "name": "js",
                            "description": "Run JavaScript",
                            "parameters": {
                                "type": "object",
                                "properties": {"code": {"type": "string"}},
                                "required": ["code"],
                            },
                        }
                    ],
                }
            ],
            "tool_choice": {
                "type": "function",
                "namespace": "mcp__node_repl",
                "name": "js",
            },
        }
    )

    assert payload["tools"] == [
        {
            "name": "mcp__node_repl__js",
            "description": "Run JavaScript",
            "input_schema": {
                "type": "object",
                "properties": {"code": {"type": "string"}},
                "required": ["code"],
            },
        }
    ]
    assert payload["tool_choice"] == {"type": "tool", "name": "mcp__node_repl__js"}


def test_responses_namespaced_tool_choice_type_tool_flattens_for_anthropic() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": "Use JS",
            "tools": [
                {
                    "type": "namespace",
                    "name": "mcp__node_repl",
                    "tools": [
                        {
                            "type": "function",
                            "name": "js",
                            "parameters": {"type": "object", "properties": {}},
                        }
                    ],
                }
            ],
            "tool_choice": {
                "type": "tool",
                "namespace": "mcp__node_repl",
                "name": "js",
            },
        }
    )

    assert payload["tool_choice"] == {"type": "tool", "name": "mcp__node_repl__js"}


def test_responses_custom_tool_converts_to_anthropic_string_tool() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": "Use apply_patch",
            "tools": [
                {
                    "type": "custom",
                    "name": "apply_patch",
                    "description": "Apply a repo patch",
                    "format": {
                        "type": "grammar",
                        "syntax": "lark",
                        "definition": "start: /.+/",
                    },
                }
            ],
            "tool_choice": {"type": "custom", "name": "apply_patch"},
        }
    )

    assert payload["tools"] == [
        {
            "name": "apply_patch",
            "description": (
                "Apply a repo patch\n\n"
                "Custom tool input format: grammar (lark): start: /.+/"
            ),
            "input_schema": {
                "type": "object",
                "properties": {
                    "input": {
                        "type": "string",
                        "description": "Free-form input for the custom tool.",
                    }
                },
                "required": ["input"],
            },
        }
    ]
    assert payload["tool_choice"] == {"type": "tool", "name": "apply_patch"}


def test_responses_namespaced_custom_tool_flattens_for_anthropic() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": "Use shell",
            "tools": [
                {
                    "type": "namespace",
                    "name": "mcp__shell",
                    "tools": [
                        {
                            "type": "custom",
                            "name": "exec",
                            "description": "Run shell text",
                            "format": {"type": "text"},
                        }
                    ],
                }
            ],
            "tool_choice": {
                "type": "custom",
                "namespace": "mcp__shell",
                "custom": {"name": "exec"},
            },
        }
    )

    assert payload["tools"][0]["name"] == "mcp__shell__exec"
    assert payload["tools"][0]["description"] == (
        "Run shell text\n\nCustom tool input format: unconstrained text."
    )
    assert payload["tool_choice"] == {"type": "tool", "name": "mcp__shell__exec"}


def test_responses_passive_codex_built_in_tools_are_ignored() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": "Hello",
            "tools": [
                {"type": "web_search", "external_web_access": True},
                {"type": "image_generation", "output_format": "png"},
                {"type": "tool_search"},
                {
                    "type": "function",
                    "name": "echo",
                    "parameters": {"type": "object", "properties": {}},
                },
            ],
        }
    )

    assert payload["tools"] == [
        {"name": "echo", "input_schema": {"type": "object", "properties": {}}}
    ]


def test_responses_namespaced_prior_function_call_flattens_tool_use_name() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {
                    "type": "function_call",
                    "call_id": "call_1",
                    "namespace": "mcp__node_repl",
                    "name": "js",
                    "arguments": '{"code":"1+1"}',
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_1",
                    "output": "2",
                },
            ],
        }
    )

    assert payload["messages"][0]["content"][0]["name"] == "mcp__node_repl__js"


def test_responses_prior_custom_tool_call_flattens_tool_use_name() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {
                    "type": "custom_tool_call",
                    "call_id": "call_1",
                    "namespace": "mcp__shell",
                    "name": "exec",
                    "input": "printf FCC",
                },
                {
                    "type": "custom_tool_call_output",
                    "call_id": "call_1",
                    "output": "FCC",
                },
            ],
        }
    )

    assert payload["messages"] == [
        {
            "role": "assistant",
            "content": [
                {
                    "type": "tool_use",
                    "id": "call_1",
                    "name": "mcp__shell__exec",
                    "input": {"input": "printf FCC"},
                }
            ],
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "call_1",
                    "content": "FCC",
                }
            ],
        },
    ]


def test_responses_groups_consecutive_prior_tool_calls() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {
                    "type": "function_call",
                    "call_id": "call_1",
                    "name": "echo",
                    "arguments": '{"value":"FCC"}',
                },
                {
                    "type": "custom_tool_call",
                    "call_id": "call_2",
                    "name": "apply_patch",
                    "input": "*** Begin Patch",
                },
            ],
        }
    )

    assert payload["messages"] == [
        {
            "role": "assistant",
            "content": [
                {
                    "type": "tool_use",
                    "id": "call_1",
                    "name": "echo",
                    "input": {"value": "FCC"},
                },
                {
                    "type": "tool_use",
                    "id": "call_2",
                    "name": "apply_patch",
                    "input": {"input": "*** Begin Patch"},
                },
            ],
        }
    ]


def test_responses_groups_consecutive_prior_tool_outputs() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {
                    "type": "function_call",
                    "call_id": "call_1",
                    "name": "echo",
                    "arguments": '{"value":"FCC"}',
                },
                {
                    "type": "function_call",
                    "call_id": "call_2",
                    "name": "echo",
                    "arguments": '{"value":"Codex"}',
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_1",
                    "output": "FCC",
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_2",
                    "output": "Codex",
                },
            ],
        }
    )

    assert len(payload["messages"]) == 2
    assert payload["messages"][0]["role"] == "assistant"
    assert len(payload["messages"][0]["content"]) == 2
    assert payload["messages"][1] == {
        "role": "user",
        "content": [
            {
                "type": "tool_result",
                "tool_use_id": "call_1",
                "content": "FCC",
            },
            {
                "type": "tool_result",
                "tool_use_id": "call_2",
                "content": "Codex",
            },
        ],
    }


def test_responses_reasoning_between_tool_call_and_output_attaches_to_tool_message() -> (
    None
):
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {
                    "type": "function_call",
                    "call_id": "call_1",
                    "name": "echo",
                    "arguments": '{"value":"FCC"}',
                },
                {
                    "type": "reasoning",
                    "content": [{"type": "reasoning_text", "text": "Need the result."}],
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_1",
                    "output": "FCC",
                },
            ],
        }
    )

    assert payload["messages"][0]["reasoning_content"] == "Need the result."
    assert payload["messages"][0]["content"][0]["id"] == "call_1"
    assert payload["messages"][1]["content"][0]["tool_use_id"] == "call_1"


def test_responses_empty_reasoning_attaches_to_prior_tool_call() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {
                    "type": "function_call",
                    "call_id": "call_1",
                    "name": "echo",
                    "arguments": '{"value":"FCC"}',
                },
                {
                    "type": "reasoning",
                    "content": [{"type": "reasoning_text", "text": ""}],
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_1",
                    "output": "FCC",
                },
            ],
        }
    )

    assert payload["messages"][0]["reasoning_content"] == ""


def test_responses_unsupported_tool_type_is_clear() -> None:
    with pytest.raises(_CONVERSION_ERROR, match="Unsupported Responses tool type"):
        _to_anthropic_payload(
            {
                "model": "nvidia_nim/test-model",
                "input": "Hello",
                "tools": [{"type": "web_search_preview"}],
            }
        )


def test_responses_malformed_prior_function_call_is_quarantined() -> None:
    payload = _to_anthropic_payload(
        {
            "model": "nvidia_nim/test-model",
            "input": [
                {"role": "user", "content": "hello"},
                {
                    "type": "function_call",
                    "call_id": "call_bad",
                    "name": "echo",
                    "arguments": "{",
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_bad",
                    "output": "stale output",
                },
                {
                    "type": "function_call",
                    "call_id": "call_good",
                    "name": "echo",
                    "arguments": '{"value":"ok"}',
                },
                {
                    "type": "function_call_output",
                    "call_id": "call_good",
                    "output": "ok",
                },
                {"role": "user", "content": "continue"},
            ],
        }
    )

    assert payload["messages"] == [
        {"role": "user", "content": "hello"},
        {
            "role": "assistant",
            "content": [
                {
                    "type": "tool_use",
                    "id": "call_good",
                    "name": "echo",
                    "input": {"value": "ok"},
                }
            ],
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "call_good",
                    "content": "ok",
                }
            ],
        },
        {"role": "user", "content": "continue"},
    ]


def test_responses_malformed_only_function_call_still_has_no_routable_message() -> None:
    with pytest.raises(_CONVERSION_ERROR, match="must contain a message"):
        _to_anthropic_payload(
            {
                "model": "nvidia_nim/test-model",
                "input": [
                    {
                        "type": "function_call",
                        "call_id": "call_bad",
                        "name": "echo",
                        "arguments": "{",
                    },
                    {
                        "type": "function_call_output",
                        "call_id": "call_bad",
                        "output": "stale output",
                    },
                ],
            }
        )
