import pytest
import os
from pathlib import Path
import tempfile
import shutil
from reposcope.core import RepoScope

@pytest.fixture
def temp_repo():
    """Create a temporary repository with test files."""
    with tempfile.TemporaryDirectory() as temp_dir:
        repo_dir = Path(temp_dir)
        
        # Create directory structure
        (repo_dir / "src").mkdir()
        (repo_dir / "docs").mkdir()
        (repo_dir / "tests").mkdir()
        (repo_dir / "build").mkdir()
        (repo_dir / ".git").mkdir()
        (repo_dir / "__pycache__").mkdir()
        
        # Create test files
        (repo_dir / "src" / "main.py").write_text("print('main')")
        (repo_dir / "src" / "utils.py").write_text("print('utils')")
        (repo_dir / "docs" / "README.md").write_text("# Documentation")
        (repo_dir / "tests" / "test_main.py").write_text("def test_main(): pass")
        (repo_dir / "build" / "output.txt").write_text("build output")
        (repo_dir / "__pycache__" / "main.cpython-39.pyc").write_text("cached")
        (repo_dir / ".gitignore").write_text("\n".join([
            "build/",
            "__pycache__/",
            "*.pyc",
        ]))
        
        yield repo_dir

@pytest.fixture
def temp_ignore_file():
    """Create a temporary ignore file."""
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
        f.write("\n".join([
            "tests/",
            "*.md",
        ]))
        return Path(f.name)

def test_gitignore_basic(temp_repo):
    """Test basic .gitignore functionality."""
    scope = RepoScope(temp_repo)
    scope.use_gitignore()
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should include
    assert "src/main.py" in files
    assert "src/utils.py" in files
    assert "docs/README.md" in files
    
    # Should exclude
    assert "build/output.txt" not in files
    assert "__pycache__/main.cpython-39.pyc" not in files

def test_extra_ignore_file(temp_repo, temp_ignore_file):
    """Test using an additional ignore file."""
    scope = RepoScope(temp_repo)
    scope.use_ignore_file(str(temp_ignore_file))
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should include
    assert "src/main.py" in files
    assert "src/utils.py" in files
    
    # Should exclude based on ignore file
    assert "tests/test_main.py" not in files
    assert "docs/README.md" not in files

def test_command_line_ignore(temp_repo):
    """Test ignore patterns from command line."""
    scope = RepoScope(temp_repo)
    scope.use_ignore_patterns(["*.py", "docs/"])
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should exclude
    assert "src/main.py" not in files
    assert "docs/README.md" not in files
    
    # Should include
    assert "build/output.txt" in files

def test_include_patterns(temp_repo):
    """Test include patterns."""
    scope = RepoScope(temp_repo)
    scope.use_include_patterns(["src/*.py"])
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should include only Python files in src
    assert "src/main.py" in files
    assert "src/utils.py" in files
    
    # Should exclude everything else
    assert "docs/README.md" not in files
    assert "tests/test_main.py" not in files
    assert "build/output.txt" not in files

def test_include_file(temp_repo):
    """Test include patterns from file."""
    include_file = temp_repo / "include.txt"
    include_file.write_text("src/*.py\ndocs/*.md")
    
    scope = RepoScope(temp_repo)
    scope.use_include_file(str(include_file))
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should include only specified patterns
    assert "src/main.py" in files
    assert "docs/README.md" in files
    
    # Should exclude everything else
    assert "tests/test_main.py" not in files
    assert "build/output.txt" not in files

def test_combining_gitignore_and_extra_ignore(temp_repo, temp_ignore_file):
    """Test combining .gitignore and extra ignore file."""
    scope = RepoScope(temp_repo)
    scope.use_gitignore()
    scope.use_ignore_file(str(temp_ignore_file))
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should exclude from both .gitignore and extra ignore
    assert "build/output.txt" not in files  # from .gitignore
    assert "docs/README.md" not in files    # from extra ignore
    assert "tests/test_main.py" not in files  # from extra ignore

def test_include_overrides_ignore(temp_repo):
    """Test that include mode overrides any ignore patterns."""
    scope = RepoScope(temp_repo)
    scope.use_gitignore()  # This should be ignored once we switch to include mode
    scope.use_include_patterns(["build/*"])  # This should take precedence
    files = {str(f.relative_to(temp_repo)) for f in scope.collect_files()}
    
    # Should include only build files, despite being in .gitignore
    assert "build/output.txt" in files
    assert len(files) == 1

def test_nonexistent_files(temp_repo):
    """Test handling of nonexistent ignore/include files."""
    scope = RepoScope(temp_repo)
    
    # Test ignore mode with nonexistent file
    scope.use_ignore_file("nonexistent.txt")
    files_ignore = scope.collect_files()
    # In ignore mode with no patterns, should include all files
    assert len(files_ignore) > 0
    
    # Create new scope for include mode test
    scope = RepoScope(temp_repo)
    scope.use_include_file("also_nonexistent.txt")
    files_include = scope.collect_files()
    # In include mode with no patterns, should include no files
    assert len(files_include) == 0

def test_empty_patterns(temp_repo):
    """Test handling of empty pattern lists."""
    scope = RepoScope(temp_repo)
    
    # Empty ignore patterns should include everything
    scope.use_ignore_patterns([])
    files1 = set(scope.collect_files())
    assert len(files1) > 0
    
    # Empty include patterns should include nothing
    scope.use_include_patterns([])
    files2 = set(scope.collect_files())
    assert len(files2) == 0