"""Tests for Compactor."""

import asyncio

from agentscope.message import Msg
from test_utils import (
    get_dash_chat_model,
    get_formatter,
    get_token_counter,
)

from reme.core.utils import get_std_logger
from reme.memory.file_based.components import Compactor

logger = get_std_logger()


# ANSI 颜色码
class Colors:
    """ANSI color codes for terminal output."""

    GREEN = "\033[92m"
    RED = "\033[91m"
    YELLOW = "\033[93m"
    BLUE = "\033[94m"
    CYAN = "\033[96m"
    BOLD = "\033[1m"
    RESET = "\033[0m"


def print_pass(test_name: str):
    """打印测试通过信息"""
    print(f"{Colors.GREEN}{Colors.BOLD}✓ {test_name} PASSED{Colors.RESET}")


def print_fail(test_name: str, error: str):
    """打印测试失败信息"""
    print(f"{Colors.RED}{Colors.BOLD}✗ {test_name} FAILED: {error}{Colors.RESET}")


def print_error(test_name: str, error: str):
    """打印测试错误信息"""
    print(f"{Colors.YELLOW}{Colors.BOLD}⚠ {test_name} ERROR: {error}{Colors.RESET}")


def print_test_header(test_name: str):
    """打印测试标题"""
    print(f"\n{Colors.CYAN}{'=' * 60}{Colors.RESET}")
    print(f"{Colors.BLUE}{Colors.BOLD}Running: {test_name}{Colors.RESET}")
    print(f"{Colors.CYAN}{'=' * 60}{Colors.RESET}")


def create_user_msg(content: str) -> Msg:
    """Create a user message."""
    return Msg(name="user", role="user", content=content)


def create_assistant_msg(content: str) -> Msg:
    """Create an assistant message."""
    return Msg(name="assistant", role="assistant", content=content)


def create_tool_use_msg(tool_name: str, tool_input: dict) -> Msg:
    """Create a message with tool_use content block."""
    return Msg(
        name="assistant",
        role="assistant",
        content=[
            {
                "type": "tool_use",
                "id": "call_123",
                "name": tool_name,
                "input": tool_input,
            },
        ],
    )


def create_tool_result_msg(tool_name: str, output: str) -> Msg:
    """Create a message with tool_result content block."""
    return Msg(
        name="tool",
        role="user",
        content=[
            {
                "type": "tool_result",
                "id": "call_123",
                "name": tool_name,
                "output": output,
            },
        ],
    )


def create_compactor():
    """Create a Compactor instance for testing."""
    return Compactor(
        memory_compact_threshold=4000,
        token_counter=get_token_counter(),
        as_llm=get_dash_chat_model(),
        as_llm_formatter=get_formatter(),
        language="zh",
    )


def test_empty_messages():
    """Test that empty messages return empty string."""
    compactor = create_compactor()
    result = asyncio.run(compactor.call(messages=[]))
    assert result == "", f"Expected empty string, got: {result}"
    print("test_empty_messages PASSED")


def test_short_conversation():
    """Test compaction of a short conversation."""
    compactor = create_compactor()
    messages = [
        create_user_msg("Hello, I need help with Python."),
        create_assistant_msg("Sure, I'd be happy to help. What do you need?"),
        create_user_msg("How do I read a file?"),
        create_assistant_msg("You can use open() function: with open('file.txt', 'r') as f: content = f.read()"),
    ]

    logger.info(f"Input messages count: {len(messages)}")
    for i, msg in enumerate(messages):
        logger.debug(
            f"Message {i}: role={msg.role}, content="
            f"{msg.content[:50] if isinstance(msg.content, str) else msg.content}...",
        )

    result = asyncio.run(compactor.call(messages=messages))

    logger.info(f"Result type: {type(result)}")
    logger.info(f"Result: {result}")

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    assert "##" in result, "Result should have markdown headers"
    print_pass("test_short_conversation")


def test_medium_conversation():
    """Test compaction of a medium-length conversation with tool calls."""
    compactor = create_compactor()
    messages = [
        create_user_msg("Help me create a Python script to process data."),
        create_assistant_msg("I'll help you create a data processing script. Let me first check the data format."),
        create_tool_use_msg("read_file", {"path": "/data/input.csv"}),
        create_tool_result_msg("read_file", "id,name,value\n1,Alice,100\n2,Bob,200\n3,Charlie,300"),
        create_assistant_msg(
            "I see the data is in CSV format. Here's a script to process it:\n"
            "```python\nimport csv\n\ndef process_data(filepath):\n"
            "    with open(filepath, 'r') as f:\n        reader = csv.DictReader(f)\n"
            "        return [row for row in reader]\n```",
        ),
        create_user_msg("Can you add a filter function?"),
        create_assistant_msg(
            "Sure, here's the updated script with filtering:\n"
            "```python\ndef filter_by_value(data, min_value):\n"
            "    return [row for row in data if int(row['value']) >= min_value]\n```",
        ),
    ]

    result = asyncio.run(compactor.call(messages=messages))

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    assert "##" in result, "Result should have markdown headers"
    print_pass("test_medium_conversation")


def test_long_conversation():
    """Test compaction of a long conversation that exceeds threshold."""
    compactor = create_compactor()
    messages = []
    for i in range(100000):
        messages.append(create_user_msg(f"Question {i}: How do I implement feature {i}?"))
        messages.append(
            create_assistant_msg(
                f"Answer {i}: Here's how to implement feature {i}. " * 20,
            ),
        )

    result = asyncio.run(compactor.call(messages=messages))

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    assert "##" in result, "Result should have markdown headers"
    print_pass("test_long_conversation")


def test_with_previous_summary():
    """Test compaction with an existing previous summary."""
    compactor = create_compactor()
    previous_summary = """## Goal
User wants to build a REST API with FastAPI.

## Constraints & Preferences
- Use Python 3.10+
- Follow RESTful best practices

## Progress
### Done
- [x] Set up project structure
- [x] Created main.py with basic FastAPI app

### In Progress
- [ ] Add user authentication

### Blocked
- (none)

## Key Decisions
- **Framework**: FastAPI for performance and type hints

## Next Steps
1. Implement JWT authentication
2. Add user endpoints

## Critical Context
- Using SQLAlchemy for database
- PostgreSQL as database backend
"""

    messages = [
        create_user_msg("Let's implement the JWT authentication now."),
        create_assistant_msg("I'll implement JWT authentication. First, let me install the required packages."),
        create_tool_use_msg("run_command", {"command": "pip install python-jose[cryptography] passlib[bcrypt]"}),
        create_tool_result_msg("run_command", "Successfully installed python-jose-3.3.0 passlib-1.7.4"),
        create_assistant_msg("Dependencies installed. Now let's create the auth module with JWT token generation."),
    ]

    result = asyncio.run(
        compactor.call(
            messages=messages,
            previous_summary=previous_summary,
        ),
    )

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    assert "##" in result, "Result should have markdown headers"
    print_pass("test_with_previous_summary")


def test_conversation_with_multiple_tool_calls():
    """Test compaction of conversation with multiple sequential tool calls."""
    compactor = create_compactor()
    messages = [
        create_user_msg("Help me debug this Python script that's failing."),
        create_assistant_msg("Let me check the script first."),
        create_tool_use_msg("read_file", {"path": "/app/main.py"}),
        create_tool_result_msg(
            "read_file",
            "def process():\n    data = load_data()\n    result = analyze(data)\n    return result",
        ),
        create_tool_use_msg("read_file", {"path": "/app/utils.py"}),
        create_tool_result_msg("read_file", "def load_data():\n    return open('data.json').read()"),
        create_tool_use_msg("run_command", {"command": "python /app/main.py"}),
        create_tool_result_msg("run_command", "FileNotFoundError: [Errno 2] No such file or directory: 'data.json'"),
        create_assistant_msg(
            "I found the issue! The script is looking for 'data.json' "
            "in the current directory instead of an absolute path.",
        ),
        create_user_msg("How should I fix it?"),
        create_assistant_msg(
            "Update load_data() to use an absolute path:\n"
            "```python\nimport os\ndef load_data():\n"
            "    script_dir = os.path.dirname(__file__)\n"
            "    return open(os.path.join(script_dir, 'data.json')).read()\n```",
        ),
    ]

    result = asyncio.run(compactor.call(messages=messages))

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    print_pass("test_conversation_with_multiple_tool_calls")


def test_low_threshold():
    """Test compaction with low memory threshold."""
    compactor = Compactor(
        memory_compact_threshold=500,
        token_counter=get_token_counter(),
        as_llm=get_dash_chat_model(),
        as_llm_formatter=get_formatter(),
    )

    messages = [
        create_user_msg("Build a web scraper."),
        create_assistant_msg("I'll create a web scraper using BeautifulSoup and requests."),
        create_user_msg("Make it handle pagination."),
        create_assistant_msg("Here's the paginated scraper implementation with error handling."),
    ]

    result = asyncio.run(compactor.call(messages=messages))

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    print("test_low_threshold PASSED")


def test_high_threshold():
    """Test compaction with high memory threshold."""
    compactor = Compactor(
        memory_compact_threshold=10000,
        token_counter=get_token_counter(),
        as_llm=get_dash_chat_model(),
        as_llm_formatter=get_formatter(),
    )

    messages = [
        create_user_msg("Create a calculator class."),
        create_assistant_msg("Here's a Calculator class with basic operations: add, subtract, multiply, divide."),
    ]

    result = asyncio.run(compactor.call(messages=messages))

    assert result, "Result should not be empty"
    assert isinstance(result, str), f"Result should be string, got: {type(result)}"
    print("test_high_threshold PASSED")


def run_all_tests():
    """Run all tests."""
    tests = [
        test_empty_messages,
        test_short_conversation,
        test_medium_conversation,
        test_long_conversation,
        test_with_previous_summary,
        test_conversation_with_multiple_tool_calls,
        test_low_threshold,
        test_high_threshold,
    ]

    passed = 0
    failed = 0

    for test in tests:
        try:
            print_test_header(test.__name__)
            test()
            passed += 1
        except AssertionError as e:
            print_fail(test.__name__, str(e))
            failed += 1
        except Exception as e:
            print_error(test.__name__, str(e))
            failed += 1

    # 打印最终统计结果
    print(f"\n{Colors.CYAN}{'=' * 60}{Colors.RESET}")
    print(f"{Colors.BOLD}Test Results Summary{Colors.RESET}")
    print(f"{Colors.CYAN}{'=' * 60}{Colors.RESET}")
    print(f"{Colors.GREEN}{Colors.BOLD}✓ Passed: {passed}{Colors.RESET}")
    if failed > 0:
        print(f"{Colors.RED}{Colors.BOLD}✗ Failed: {failed}{Colors.RESET}")
    else:
        print(f"{Colors.GREEN}✗ Failed: {failed}{Colors.RESET}")
    print(f"{Colors.CYAN}{'=' * 60}{Colors.RESET}")

    if failed == 0:
        print(f"\n{Colors.GREEN}{Colors.BOLD}🎉 All tests passed!{Colors.RESET}")
    else:
        print(f"\n{Colors.RED}{Colors.BOLD}💥 Some tests failed!{Colors.RESET}")


if __name__ == "__main__":
    run_all_tests()
