import os
import pytest
from pathlib import Path
from unittest.mock import patch, mock_open
from ra_aid.tools.file_str_replace import file_str_replace

@pytest.fixture
def temp_test_dir(tmp_path):
    """Create a temporary test directory."""
    test_dir = tmp_path / "test_replace_dir"
    test_dir.mkdir(exist_ok=True)
    return test_dir

def test_basic_replacement(temp_test_dir):
    """Test basic string replacement functionality."""
    test_file = temp_test_dir / "test.txt"
    initial_content = "Hello world! This is a test."
    test_file.write_text(initial_content)
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "world",
        "new_str": "universe"
    })
    
    assert result["success"] is True
    assert test_file.read_text() == "Hello universe! This is a test."
    assert "Successfully replaced" in result["message"]

def test_file_not_found():
    """Test handling of non-existent file."""
    result = file_str_replace.invoke({
        "filepath": "nonexistent.txt",
        "old_str": "test",
        "new_str": "replacement"
    })
    
    assert result["success"] is False
    assert "File not found" in result["message"]

def test_string_not_found(temp_test_dir):
    """Test handling of string not present in file."""
    test_file = temp_test_dir / "test.txt"
    test_file.write_text("Hello world!")
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "nonexistent",
        "new_str": "replacement"
    })
    
    assert result["success"] is False
    assert "String not found" in result["message"]

def test_multiple_occurrences(temp_test_dir):
    """Test handling of multiple string occurrences."""
    test_file = temp_test_dir / "test.txt"
    test_file.write_text("test test test")
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "test",
        "new_str": "replacement"
    })
    
    assert result["success"] is False
    assert "appears" in result["message"]
    assert "must be unique" in result["message"]

def test_empty_strings(temp_test_dir):
    """Test handling of empty strings."""
    test_file = temp_test_dir / "test.txt"
    test_file.write_text("Hello world!")
    
    # Test empty old string
    result1 = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "",
        "new_str": "replacement"
    })
    assert result1["success"] is False
    
    # Test empty new string
    result2 = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "world",
        "new_str": ""
    })
    assert result2["success"] is True
    assert test_file.read_text() == "Hello !"

def test_special_characters(temp_test_dir):
    """Test handling of special characters."""
    test_file = temp_test_dir / "test.txt"
    initial_content = "Hello\nworld!\t\r\nSpecial chars: $@#%"
    test_file.write_text(initial_content)
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "Special chars: $@#%",
        "new_str": "Replaced!"
    })
    
    assert result["success"] is True
    assert "Special chars: $@#%" not in test_file.read_text()
    assert "Replaced!" in test_file.read_text()

@patch('pathlib.Path.read_text')
def test_io_error(mock_read_text, temp_test_dir):
    """Test handling of IO errors during read."""
    # Create and write to file first
    test_file = temp_test_dir / "test.txt"
    test_file.write_text("some test content")
    
    # Then mock read_text to raise error
    mock_read_text.side_effect = IOError("Failed to read file")
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "test",
        "new_str": "replacement"
    })
    
    assert result["success"] is False
    assert "Failed to read file" in result["message"]

def test_permission_error(temp_test_dir):
    """Test handling of permission errors."""
    test_file = temp_test_dir / "readonly.txt"
    test_file.write_text("test content")
    os.chmod(test_file, 0o444)  # Make file read-only
    
    try:
        result = file_str_replace.invoke({
            "filepath": str(test_file),
            "old_str": "test",
            "new_str": "replacement"
        })
        
        assert result["success"] is False
        assert "Permission" in result["message"] or "Error" in result["message"]
    finally:
        os.chmod(test_file, 0o644)  # Restore permissions for cleanup

def test_unicode_strings(temp_test_dir):
    """Test handling of Unicode strings."""
    test_file = temp_test_dir / "unicode.txt"
    initial_content = "Hello 世界! Unicode テスト"
    test_file.write_text(initial_content)
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": "世界",
        "new_str": "ワールド"
    })
    
    assert result["success"] is True
    assert "世界" not in test_file.read_text()
    assert "ワールド" in test_file.read_text()

def test_long_string_truncation(temp_test_dir):
    """Test handling and truncation of very long strings."""
    test_file = temp_test_dir / "test.txt"
    long_string = "x" * 100
    test_file.write_text(f"prefix {long_string} suffix")
    
    result = file_str_replace.invoke({
        "filepath": str(test_file),
        "old_str": long_string,
        "new_str": "replaced"
    })
    
    assert result["success"] is True
    assert test_file.read_text() == "prefix replaced suffix"
