from pathlib import Path
from unittest.mock import patch

import pytest

from c2m_by_nery.core.config import ConfigManager
from c2m_by_nery.core.scanner import Scanner


def test_scanner_includes_text_files(tmp_path: Path) -> None:
    (tmp_path / "foo.py").write_text("x")
    (tmp_path / "bar.md").write_text("# bar")
    config = ConfigManager()

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [f.name for f in files]
    assert "foo.py" in paths
    assert "bar.md" in paths


def test_scanner_excludes_pyc(tmp_path: Path) -> None:
    (tmp_path / "foo.py").write_text("x")
    (tmp_path / "foo.pyc").write_bytes(b"\x00\x00")
    config = ConfigManager()

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [f.name for f in files]
    assert "foo.py" in paths
    assert "foo.pyc" not in paths


def test_scanner_excludes_system_dirs(tmp_path: Path) -> None:
    (tmp_path / "normal.py").write_text("x")
    (tmp_path / "__pycache__").mkdir()
    (tmp_path / "__pycache__" / "x.pyc").write_bytes(b"\x00")
    (tmp_path / ".git").mkdir()
    config = ConfigManager()

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [str(f.relative_to(tmp_path)) for f in files]
    assert "normal.py" in paths
    assert not any("__pycache__" in p for p in paths)
    assert not any(".git" in p for p in paths)


def test_scanner_excludes_c2m_output(tmp_path: Path) -> None:
    (tmp_path / "src").mkdir()
    (tmp_path / "src" / "code.py").write_text("x = 1")
    (tmp_path / ".c2m" / "outputs" / "20240101-120000").mkdir(parents=True)
    (tmp_path / ".c2m" / "outputs" / "20240101-120000" / "code.md").write_text("# code")
    config = ConfigManager()

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [str(f.relative_to(tmp_path)).replace("\\", "/") for f in files]
    assert "src/code.py" in paths
    assert not any(".c2m" in p for p in paths)


def test_scanner_respects_ignore_patterns(tmp_path: Path) -> None:
    (tmp_path / "include.py").write_text("x")
    (tmp_path / "exclude.py").write_text("y")
    config = ConfigManager(ignore_patterns=["**/exclude.py"])

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [f.name for f in files]
    assert "include.py" in paths
    assert "exclude.py" not in paths


def test_scanner_excludes_binary_content(tmp_path: Path) -> None:
    (tmp_path / "text.txt").write_text("plain text")
    (tmp_path / "binary_like.xyz").write_bytes(b"some\x00binary\x00content")
    config = ConfigManager()

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [f.name for f in files]
    assert "text.txt" in paths
    assert "binary_like.xyz" not in paths


def test_scanner_excludes_lock_files_by_default(tmp_path: Path) -> None:
    (tmp_path / "src").mkdir()
    (tmp_path / "src" / "code.py").write_text("x = 1")
    (tmp_path / "package-lock.json").write_text("{}")
    (tmp_path / "yarn.lock").write_text("# yarn")
    (tmp_path / "composer.lock").write_text("{}")
    config = ConfigManager()

    with patch.object(Scanner, "_is_gitignored", return_value=False):
        scanner = Scanner(tmp_path, config)
        files = scanner.scan()

    paths = [str(f.relative_to(tmp_path)).replace("\\", "/") for f in files]
    assert "src/code.py" in paths
    assert "package-lock.json" not in paths
    assert "yarn.lock" not in paths
    assert "composer.lock" not in paths
