"""
Base test class for function calling tests.

This module provides test cases for function calling functionality
across different backends.
"""

import json
import sys
from pathlib import Path

# Add current directory for local imports
_TEST_DIR = Path(__file__).parent
sys.path.insert(0, str(_TEST_DIR))

from basic_crud import ResponseAPIBaseTest


class FunctionCallingBaseTest(ResponseAPIBaseTest):

    def test_basic_function_call(self):
        """
        Test basic function calling workflow.

        This test follows the pattern from function_call_test.py:
        1. Define a function tool (get_horoscope)
        2. Send user message asking for horoscope
        3. Model should return function_call
        4. Execute function locally and provide output
        5. Model should generate final response using the function output
        """
        # 1. Define a list of callable tools for the model
        tools = [
            {
                "type": "function",
                "name": "get_horoscope",
                "description": "Get today's horoscope for an astrological sign.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "sign": {
                            "type": "string",
                            "description": "An astrological sign like Taurus or Aquarius",
                        },
                    },
                    "required": ["sign"],
                },
            },
        ]
        system_prompt = (
            "You are a helpful assistant that can call functions. "
            "When a user asks for horoscope information, call the function. "
            "IMPORTANT: Don't reply directly to the user, only call the function. "
        )

        # Create a running input list we will add to over time
        input_list = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": "What is my horoscope? I am an Aquarius."},
        ]

        # 2. Prompt the model with tools defined
        resp = self.create_response(input=input_list, tools=tools)

        # Should successfully make the request
        self.assertIsNone(resp.error)

        # Basic response structure
        self.assertIsNotNone(resp.id)
        self.assertEqual(resp.status, "completed")
        self.assertIsNotNone(resp.output)

        # Verify output array is not empty
        output = resp.output
        self.assertIsInstance(output, list)
        self.assertGreater(len(output), 0)

        # Check for function_call in output
        function_calls = [item for item in output if item.type == "function_call"]
        self.assertGreater(
            len(function_calls), 0, "Response should contain at least one function_call"
        )

        # Verify function_call structure
        function_call = function_calls[0]
        self.assertIsNotNone(function_call.call_id)
        self.assertIsNotNone(function_call.name)
        self.assertEqual(function_call.name, "get_horoscope")
        self.assertIsNotNone(function_call.arguments)

        # Parse arguments
        args = json.loads(function_call.arguments)
        self.assertIn("sign", args)
        self.assertEqual(args["sign"].lower(), "aquarius")

        # 3. Save function call outputs for subsequent requests
        input_list.append(function_call)

        # 4. Execute the function logic for get_horoscope
        horoscope = f"{args['sign']}: Next Tuesday you will befriend a baby otter."

        # 5. Provide function call results to the model
        input_list.append(
            {
                "type": "function_call_output",
                "call_id": function_call.call_id,
                "output": json.dumps({"horoscope": horoscope}),
            }
        )

        # 6. Make second request with function output
        resp2 = self.create_response(
            input=input_list,
            instructions="Respond only with a horoscope generated by a tool.",
            tools=tools,
        )
        self.assertIsNone(resp2.error)
        self.assertEqual(resp2.status, "completed")

        # The model should be able to give a response using the function output
        output2 = resp2.output
        self.assertGreater(len(output2), 0)

        # Find message output
        messages = [item for item in output2 if item.type == "message"]
        self.assertGreater(
            len(messages), 0, "Response should contain at least one message"
        )

        # Verify message contains the horoscope
        message = messages[0]
        self.assertIsNotNone(message.content)
        content_parts = message.content
        self.assertGreater(len(content_parts), 0)

        # Get text from content
        text_parts = [part.text for part in content_parts if part.type == "output_text"]
        full_text = " ".join(text_parts).lower()

        # Should mention the horoscope or baby otter
        self.assertTrue(
            "baby otter" in full_text or "aquarius" in full_text,
            "Response should reference the horoscope content",
        )
