"""Unit tests for the StorageManager class."""

import os
import shutil
from pathlib import Path

import pytest

from mcp_server_neurolorap.storage import StorageManager


@pytest.fixture
def storage_manager(project_root: Path) -> StorageManager:
    """Create a StorageManager instance for testing."""
    return StorageManager(project_root)


@pytest.fixture
def subproject_storage(project_root: Path) -> StorageManager:
    """Create a StorageManager instance with subproject."""
    return StorageManager(project_root, subproject_id="test-sub")


def test_init_basic(
    storage_manager: StorageManager, project_root: Path
) -> None:
    """Test basic initialization of StorageManager."""
    assert storage_manager.project_root == project_root
    assert storage_manager.project_name == project_root.name
    assert storage_manager.mcp_docs_dir == Path.home() / ".mcp-docs"
    assert (
        storage_manager.project_docs_dir
        == storage_manager.mcp_docs_dir / project_root.name
    )
    assert storage_manager.neurolora_link == project_root / ".neurolora"


def test_init_with_subproject(
    subproject_storage: StorageManager, project_root: Path
) -> None:
    """Test initialization with subproject ID."""
    expected_name = f"{project_root.name}-test-sub"
    assert subproject_storage.project_name == expected_name
    assert subproject_storage.project_docs_dir == (
        subproject_storage.mcp_docs_dir / expected_name
    )


def test_setup_creates_directories(storage_manager: StorageManager) -> None:
    """Test that setup creates all required directories."""
    storage_manager.setup()

    # Check main directories
    assert storage_manager.mcp_docs_dir.exists()
    assert storage_manager.project_docs_dir.exists()

    # Check initialization marker
    assert (storage_manager.project_docs_dir / ".initialized").exists()


def test_setup_creates_symlink(storage_manager: StorageManager) -> None:
    """Test that setup creates the .neurolora symlink correctly."""
    storage_manager.setup()

    # Check symlink exists and points to correct location
    assert storage_manager.neurolora_link.exists()
    assert storage_manager.neurolora_link.is_symlink()
    assert (
        storage_manager.neurolora_link.resolve()
        == storage_manager.project_docs_dir
    )


def test_setup_creates_task_files(storage_manager: StorageManager) -> None:
    """Test that setup creates TODO.md and DONE.md files."""
    storage_manager.setup()

    # Check task files exist
    assert (storage_manager.project_docs_dir / "TODO.md").exists()
    assert (storage_manager.project_docs_dir / "DONE.md").exists()


def test_setup_creates_ignore_file(storage_manager: StorageManager) -> None:
    """Test that setup creates .neuroloraignore file."""
    storage_manager.setup()

    # Check ignore file exists
    assert (storage_manager.project_root / ".neuroloraignore").exists()


def test_get_output_path(storage_manager: StorageManager) -> None:
    """Test getting output file path."""
    filename = "test.md"
    expected_path = storage_manager.project_docs_dir / filename
    assert storage_manager.get_output_path(filename) == expected_path


def test_symlink_update(
    storage_manager: StorageManager, project_root: Path
) -> None:
    """Test updating existing symlink."""
    # Create initial setup
    storage_manager.setup()

    # Create a different directory and update symlink
    new_target = project_root / "new_target"
    new_target.mkdir()

    # Update symlink
    storage_manager._create_or_update_symlink(
        storage_manager.neurolora_link, new_target, ".neurolora"
    )

    # Check symlink points to new target
    assert storage_manager.neurolora_link.resolve() == new_target

    # Cleanup
    new_target.rmdir()


def test_error_handling_invalid_symlink(
    storage_manager: StorageManager,
) -> None:
    """Test handling invalid symlink scenarios."""
    # Create a file instead of symlink
    storage_manager.neurolora_link.touch()

    # Setup should handle this by removing and recreating
    storage_manager.setup()

    assert storage_manager.neurolora_link.is_symlink()
    assert (
        storage_manager.neurolora_link.resolve()
        == storage_manager.project_docs_dir
    )


def test_error_handling_permission_denied(
    storage_manager: StorageManager,
) -> None:
    """Test handling permission denied errors."""
    if os.name != "nt":  # Skip on Windows
        # Make project directory read-only
        storage_manager.project_docs_dir.mkdir(parents=True, exist_ok=True)
        storage_manager.project_docs_dir.chmod(0o444)

        with pytest.raises(Exception) as exc_info:
            storage_manager._create_template_file(
                "todo.template.md", "TODO.md"
            )
        assert "Permission denied" in str(exc_info.value)

        # Cleanup
        storage_manager.project_docs_dir.chmod(0o777)


def test_template_file_missing_template(
    storage_manager: StorageManager, caplog: pytest.LogCaptureFixture
) -> None:
    """Test handling missing template files."""
    storage_manager.setup()

    storage_manager._create_template_file("nonexistent.template", "output.md")

    assert "Template file not found" in caplog.text


def test_cleanup_between_tests(storage_manager: StorageManager) -> None:
    """Test cleanup between tests."""
    storage_manager.setup()

    # Clean up test directories
    if storage_manager.project_docs_dir.exists():
        shutil.rmtree(storage_manager.project_docs_dir)
    if storage_manager.neurolora_link.exists():
        storage_manager.neurolora_link.unlink()

    assert not storage_manager.project_docs_dir.exists()
    assert not storage_manager.neurolora_link.exists()


@pytest.mark.parametrize(
    "subproject_id", ["test-sub", "feature-123", "debug-mode"]
)
def test_multiple_subprojects(project_root: Path, subproject_id: str) -> None:
    """Test handling multiple subprojects."""
    storage = StorageManager(project_root, subproject_id=subproject_id)
    storage.setup()

    expected_name = f"{project_root.name}-{subproject_id}"
    assert storage.project_name == expected_name
    assert storage.project_docs_dir.exists()
    assert storage.neurolora_link.exists()

    # Cleanup
    if storage.project_docs_dir.exists():
        shutil.rmtree(storage.project_docs_dir)
    if storage.neurolora_link.exists():
        storage.neurolora_link.unlink()


def test_concurrent_access(project_root: Path) -> None:
    """Test handling concurrent access to storage."""
    # Create multiple storage managers for same project
    storage1 = StorageManager(project_root)
    storage2 = StorageManager(project_root)

    # Setup both instances
    storage1.setup()
    storage2.setup()

    # Verify both instances work correctly
    assert storage1.project_docs_dir.exists()
    assert storage2.project_docs_dir.exists()
    assert storage1.neurolora_link.exists()
    assert storage2.neurolora_link.exists()

    # Verify they point to same location
    assert storage1.project_docs_dir == storage2.project_docs_dir
    assert (
        storage1.neurolora_link.resolve() == storage2.neurolora_link.resolve()
    )

    # Cleanup
    if storage1.project_docs_dir.exists():
        shutil.rmtree(storage1.project_docs_dir)
    if storage1.neurolora_link.exists():
        storage1.neurolora_link.unlink()
