"""
Manual test: Verify tool_call() return format for all LLM response scenarios.

Tests _normalize_content_to_str() and the simplified_response logic in tool_call().
Run: python tests/test_tool_call_return_formats.py
"""

import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))

from ultragpt.core.core import _normalize_content_to_str


def test_normalize_content_to_str():
    """Test the content normalization helper for all possible content types."""
    print("=" * 60)
    print("Testing _normalize_content_to_str()")
    print("=" * 60)
    
    test_cases = [
        # (input, expected_output, description)
        (None, "", "None -> empty string"),
        ("", "", "Empty string -> empty string"),
        ("Hello world", "Hello world", "Normal string -> same string"),
        ("  whitespace  ", "  whitespace  ", "String with whitespace -> preserved"),
        ("None", "None", "Literal 'None' string -> preserved (not empty)"),
        
        # List content (Anthropic/Claude format)
        ([], "", "Empty list -> empty string"),
        ([{"type": "text", "text": "Hello"}], "Hello", "Single text block -> extracted text"),
        (
            [{"type": "text", "text": "First"}, {"type": "text", "text": "Second"}],
            "First\nSecond",
            "Multiple text blocks -> joined with newline"
        ),
        (
            [{"type": "image", "source": "..."}, {"type": "text", "text": "Caption"}],
            "Caption",
            "Mixed blocks -> only text extracted"
        ),
        ([{"type": "tool_use", "id": "123"}], "", "Tool-use only blocks -> empty string"),
        (["raw string in list"], "raw string in list", "Raw string in list -> extracted"),
        ([{"text": "no type field"}], "no type field", "Dict with text but no type -> extracted"),
        
        # Other types
        (123, "123", "Integer -> str conversion"),
        (True, "True", "Boolean -> str conversion"),
        ({"key": "value"}, "{'key': 'value'}", "Dict -> str conversion"),
    ]
    
    passed = 0
    failed = 0
    
    for i, (input_val, expected, description) in enumerate(test_cases, 1):
        result = _normalize_content_to_str(input_val)
        status = "PASS" if result == expected else "FAIL"
        if status == "PASS":
            passed += 1
        else:
            failed += 1
            
        print(f"  [{status}] {i}. {description}")
        if status == "FAIL":
            print(f"         Input:    {repr(input_val)}")
            print(f"         Expected: {repr(expected)}")
            print(f"         Got:      {repr(result)}")
    
    print(f"\n  Results: {passed} passed, {failed} failed out of {len(test_cases)}")
    return failed == 0


def test_simplified_response_logic():
    """
    Simulate the simplified_response logic from tool_call() for all 7 scenarios.
    This tests the logic without making actual API calls.
    """
    print("\n" + "=" * 60)
    print("Testing simplified_response logic (simulated)")
    print("=" * 60)
    
    # Simulate the logic from core.py tool_call()
    def simulate_tool_call_response(response_message, allow_multiple=True):
        """Replicate the exact logic from core.py tool_call()"""
        if response_message.get("tool_calls"):
            tool_calls_list = response_message.get("tool_calls")
            accompanying_content = _normalize_content_to_str(response_message.get("content"))
            has_content = bool(accompanying_content and accompanying_content.strip())
            
            if allow_multiple:
                if has_content:
                    return {"tool_calls": tool_calls_list, "content": accompanying_content}
                else:
                    return tool_calls_list
            else:
                single_tc = tool_calls_list[0] if tool_calls_list else None
                if has_content and single_tc:
                    return {"tool_calls": [single_tc], "content": accompanying_content}
                elif single_tc:
                    return single_tc
                else:
                    return {"content": accompanying_content or ""}
        else:
            content = _normalize_content_to_str(response_message.get("content"))
            if content and content.strip():
                return {"content": content}
            else:
                return {"content": ""}
    
    sample_tc = [{"id": "call_123", "type": "function", "function": {"name": "test", "arguments": "{}"}}]
    
    test_cases = [
        # (response_message, allow_multiple, expected_type, description)
        (
            {"role": "assistant", "content": "Here is the answer"},
            True,
            {"content": "Here is the answer"},
            "Case 1: Text only"
        ),
        (
            {"role": "assistant", "content": None, "tool_calls": sample_tc},
            True,
            sample_tc,  # list
            "Case 2: Tool calls only (content=None)"
        ),
        (
            {"role": "assistant", "content": "Let me check", "tool_calls": sample_tc},
            True,
            {"tool_calls": sample_tc, "content": "Let me check"},
            "Case 3: Mixed (text + tool calls)"
        ),
        (
            {"role": "assistant", "content": None},
            True,
            {"content": ""},
            "Case 4: Empty (content=None, no tool_calls)"
        ),
        (
            {"role": "assistant", "content": ""},
            True,
            {"content": ""},
            "Case 5: Empty string content"
        ),
        (
            {"role": "assistant", "content": [{"type": "text", "text": "Hi from Claude"}]},
            True,
            {"content": "Hi from Claude"},
            "Case 6: List content blocks (no tool calls)"
        ),
        (
            {"role": "assistant", "content": [{"type": "text", "text": "Checking..."}], "tool_calls": sample_tc},
            True,
            {"tool_calls": sample_tc, "content": "Checking..."},
            "Case 7: List content blocks + tool calls"
        ),
        # allow_multiple=False variants
        (
            {"role": "assistant", "content": None, "tool_calls": sample_tc},
            False,
            sample_tc[0],  # single dict
            "Case 2b: Tool calls only (allow_multiple=False)"
        ),
        (
            {"role": "assistant", "content": "Let me check", "tool_calls": sample_tc},
            False,
            {"tool_calls": [sample_tc[0]], "content": "Let me check"},
            "Case 3b: Mixed (allow_multiple=False)"
        ),
        (
            {"role": "assistant", "content": "", "tool_calls": sample_tc},
            True,
            sample_tc,  # list - empty content treated as no content
            "Case 8: Empty string content + tool calls (backward compat)"
        ),
        (
            {"role": "assistant", "content": "   ", "tool_calls": sample_tc},
            True,
            sample_tc,  # list - whitespace-only content treated as no content
            "Case 9: Whitespace-only content + tool calls"
        ),
    ]
    
    passed = 0
    failed = 0
    
    for i, (msg, allow_multi, expected, description) in enumerate(test_cases, 1):
        result = simulate_tool_call_response(msg, allow_multi)
        status = "PASS" if result == expected else "FAIL"
        if status == "PASS":
            passed += 1
        else:
            failed += 1
            
        print(f"  [{status}] {i}. {description}")
        if status == "FAIL":
            print(f"         Expected: {repr(expected)}")
            print(f"         Got:      {repr(result)}")
    
    print(f"\n  Results: {passed} passed, {failed} failed out of {len(test_cases)}")
    return failed == 0


def test_never_returns_none():
    """Verify that NO input combination can produce a None return value."""
    print("\n" + "=" * 60)
    print("Testing: tool_call() NEVER returns None")
    print("=" * 60)
    
    def simulate_tool_call_response(response_message, allow_multiple=True):
        if response_message.get("tool_calls"):
            tool_calls_list = response_message.get("tool_calls")
            accompanying_content = _normalize_content_to_str(response_message.get("content"))
            has_content = bool(accompanying_content and accompanying_content.strip())
            
            if allow_multiple:
                if has_content:
                    return {"tool_calls": tool_calls_list, "content": accompanying_content}
                else:
                    return tool_calls_list
            else:
                single_tc = tool_calls_list[0] if tool_calls_list else None
                if has_content and single_tc:
                    return {"tool_calls": [single_tc], "content": accompanying_content}
                elif single_tc:
                    return single_tc
                else:
                    return {"content": accompanying_content or ""}
        else:
            content = _normalize_content_to_str(response_message.get("content"))
            if content and content.strip():
                return {"content": content}
            else:
                return {"content": ""}
    
    # Exhaustive edge cases
    edge_cases = [
        {"role": "assistant"},  # Missing content and tool_calls
        {"role": "assistant", "content": None},
        {"role": "assistant", "content": None, "tool_calls": None},
        {"role": "assistant", "content": None, "tool_calls": []},
        {"role": "assistant", "content": "", "tool_calls": []},
        {"role": "assistant", "content": "", "tool_calls": None},
        {"role": "assistant", "tool_calls": []},
        {"role": "assistant", "content": []},
        {"role": "assistant", "content": [{"type": "image"}]},  # Non-text blocks only
        {"role": "assistant", "content": 0},
        {"role": "assistant", "content": False},
    ]
    
    none_count = 0
    for i, msg in enumerate(edge_cases, 1):
        for allow_multi in [True, False]:
            result = simulate_tool_call_response(msg, allow_multi)
            if result is None:
                none_count += 1
                print(f"  [FAIL] Case {i} (allow_multiple={allow_multi}): returned None!")
                print(f"         Input: {repr(msg)}")
    
    if none_count == 0:
        print(f"  [PASS] All {len(edge_cases) * 2} edge cases returned non-None values")
    else:
        print(f"  [FAIL] {none_count} cases returned None!")
    
    return none_count == 0


if __name__ == "__main__":
    print("\n" + "=" * 60)
    print("  UltraGPT tool_call() Return Format Test Suite")
    print("=" * 60 + "\n")
    
    results = []
    results.append(("_normalize_content_to_str", test_normalize_content_to_str()))
    results.append(("simplified_response logic", test_simplified_response_logic()))
    results.append(("never returns None", test_never_returns_none()))
    
    print("\n" + "=" * 60)
    print("  SUMMARY")
    print("=" * 60)
    all_passed = True
    for name, passed in results:
        status = "PASS" if passed else "FAIL"
        if not passed:
            all_passed = False
        print(f"  [{status}] {name}")
    
    print(f"\n  Overall: {'ALL TESTS PASSED' if all_passed else 'SOME TESTS FAILED'}")
    print("=" * 60)
    
    sys.exit(0 if all_passed else 1)
