# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
from llama_stack.models.llama.llama3.tool_utils import ToolUtils


class TestMaybeExtractCustomToolCall:
    def test_valid_single_tool_call(self):
        input_string = '[get_weather(location="San Francisco", units="celsius")]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "get_weather"
        assert result[1] == {"location": "San Francisco", "units": "celsius"}

    def test_valid_multiple_tool_calls(self):
        input_string = '[search(query="python programming"), get_time(timezone="UTC")]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        # Note: maybe_extract_custom_tool_call currently only returns the first tool call
        assert result is not None
        assert len(result) == 2
        assert result[0] == "search"
        assert result[1] == {"query": "python programming"}

    def test_different_value_types(self):
        input_string = '[analyze_data(count=42, enabled=True, ratio=3.14, name="test", options=None)]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "analyze_data"
        assert result[1] == {"count": 42, "enabled": True, "ratio": 3.14, "name": "test", "options": None}

    def test_nested_structures(self):
        input_string = '[complex_function(filters={"min": 10, "max": 100}, tags=["important", "urgent"])]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        # This test checks that nested structures are handled
        assert result is not None
        assert len(result) == 2
        assert result[0] == "complex_function"
        assert "filters" in result[1]
        assert sorted(result[1]["filters"].items()) == sorted({"min": 10, "max": 100}.items())

        assert "tags" in result[1]
        assert result[1]["tags"] == ["important", "urgent"]

    def test_hyphenated_function_name(self):
        input_string = '[weather-forecast(city="London")]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "weather-forecast"  # Function name remains hyphenated
        assert result[1] == {"city": "London"}

    def test_empty_input(self):
        input_string = "[]"
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is None

    def test_invalid_format(self):
        invalid_inputs = [
            'get_weather(location="San Francisco")',  # Missing outer brackets
            '{get_weather(location="San Francisco")}',  # Wrong outer brackets
            '[get_weather(location="San Francisco"]',  # Unmatched brackets
            '[get_weather{location="San Francisco"}]',  # Wrong inner brackets
            "just some text",  # Not a tool call format at all
        ]

        for input_string in invalid_inputs:
            result = ToolUtils.maybe_extract_custom_tool_call(input_string)
            assert result is None

    def test_quotes_handling(self):
        input_string = '[search(query="Text with \\"quotes\\" inside")]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        # This test checks that escaped quotes are handled correctly
        assert result is not None

    def test_single_quotes_in_arguments(self):
        input_string = "[add-note(name='demonote', content='demonstrating Llama Stack and MCP integration')]"
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "add-note"  # Function name remains hyphenated
        assert result[1] == {"name": "demonote", "content": "demonstrating Llama Stack and MCP integration"}

    def test_json_format(self):
        input_string = '{"type": "function", "name": "search_web", "parameters": {"query": "AI research"}}'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "search_web"
        assert result[1] == {"query": "AI research"}

    def test_python_list_format(self):
        input_string = "[calculate(x=10, y=20)]"
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "calculate"
        assert result[1] == {"x": 10, "y": 20}

    def test_complex_nested_structures(self):
        input_string = '[advanced_query(config={"filters": {"categories": ["books", "electronics"], "price_range": {"min": 10, "max": 500}}, "sort": {"field": "relevance", "order": "desc"}})]'
        result = ToolUtils.maybe_extract_custom_tool_call(input_string)

        assert result is not None
        assert len(result) == 2
        assert result[0] == "advanced_query"

        # Verify the overall structure
        assert "config" in result[1]
        assert isinstance(result[1]["config"], dict)

        # Verify the first level of nesting
        config = result[1]["config"]
        assert "filters" in config
        assert "sort" in config

        # Verify the second level of nesting (filters)
        filters = config["filters"]
        assert "categories" in filters
        assert "price_range" in filters

        # Verify the list within the dict
        assert filters["categories"] == ["books", "electronics"]

        # Verify the nested dict within another dict
        assert filters["price_range"]["min"] == 10
        assert filters["price_range"]["max"] == 500

        # Verify the sort dictionary
        assert config["sort"]["field"] == "relevance"
        assert config["sort"]["order"] == "desc"
