import pytest
from typing import cast, Any

from pydantic import BaseModel, Field
from langchain_core.tools import StructuredTool

from frisk_sdk.adapters.langchain.wrap_tool_with_reasoning import (
    wrap_tool_with_reasoning,
)
from frisk_sdk.core.llm_reasoning import LLM_REASONING_ARG_NAME


@pytest.mark.unit
def test_wrap_tool_with_reasoning_carries_over_metadata_and_strips_reasoning_on_invoke():
    calls: list[dict] = []

    class Args(BaseModel):
        x: int = Field(description="some int")
        y: str = Field(description="some str")

    def spy(x: int, y: str) -> str:
        calls.append({"x": x, "y": y})
        return f"{x}:{y}"

    tool = StructuredTool.from_function(
        func=spy,
        name="my_tool",
        description="Does a thing.",
        args_schema=Args,
        infer_schema=False,
    )

    wrapped_tool = wrap_tool_with_reasoning(tool)

    # name should be preserved
    assert wrapped_tool.name == "my_tool"

    # description should include original + IMPORTANT suffix
    assert "Does a thing." in wrapped_tool.description
    assert "IMPORTANT" in wrapped_tool.description
    assert LLM_REASONING_ARG_NAME in wrapped_tool.description

    # args schema should include the original fields plus a required `reasoning` string
    assert wrapped_tool.args_schema is not None
    schema = cast(type[BaseModel], wrapped_tool.args_schema).model_json_schema()

    props = schema.get("properties", {})
    assert "x" in props
    assert "y" in props
    assert LLM_REASONING_ARG_NAME in props

    # invoking should call underlying tool without `reasoning`
    out = wrapped_tool.invoke(
        cast(Any, {"x": 7, "y": "hi", LLM_REASONING_ARG_NAME: "because"})
    )
    assert out == "7:hi"
    assert calls == [{"x": 7, "y": "hi"}]


@pytest.mark.unit
def test_wrap_tool_with_reasoning_requires_args_schema():
    class FakeTool:
        # wrap_tool_with_reasoning only checks .args_schema before doing anything else,
        # so a tiny stub keeps this test stable across LangChain versions.
        args_schema = None

    with pytest.raises(ValueError, match="args_schema"):
        wrap_tool_with_reasoning(cast(StructuredTool, FakeTool()))
