from pathlib import Path

from app.services import repo_scanner


def _touch(path: Path, content: str = "# doc") -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(content, encoding="utf-8")


def test_scan_repository_docs_detects_supported_patterns(tmp_path):
    _touch(tmp_path / "README.md")
    _touch(tmp_path / "architecture.md")
    _touch(tmp_path / "CONTRIBUTING.md")
    _touch(tmp_path / "DEPLOYMENT.md")
    _touch(tmp_path / "ONBOARDING.md")
    _touch(tmp_path / "docs" / "api.md")
    _touch(tmp_path / "docs" / "nested" / "guide.md")
    _touch(tmp_path / "adr" / "0001-decision.md")
    _touch(tmp_path / "runbooks" / "deploy" / "incident.md")

    _touch(tmp_path / "src" / "README.md")
    _touch(tmp_path / "random.md")
    _touch(tmp_path / "docs" / "notes.txt")

    docs = repo_scanner.scan_repository_docs(tmp_path)

    assert docs == [
        "CONTRIBUTING.md",
        "DEPLOYMENT.md",
        "ONBOARDING.md",
        "README.md",
        "adr/0001-decision.md",
        "architecture.md",
        "docs/api.md",
        "docs/nested/guide.md",
        "runbooks/deploy/incident.md",
    ]


def test_scan_repository_docs_is_deterministic(tmp_path):
    _touch(tmp_path / "docs" / "z.md")
    _touch(tmp_path / "docs" / "a.md")
    _touch(tmp_path / "runbooks" / "ops" / "b.md")

    first = repo_scanner.scan_repository_docs(tmp_path)
    second = repo_scanner.scan_repository_docs(tmp_path)

    assert first == second


def test_normalize_repo_relative_path_uses_forward_slashes():
    normalized = repo_scanner.normalize_repo_relative_path("docs\\nested\\api.md")

    assert normalized == "docs/nested/api.md"


def test_scan_repository_docs_logs_traversal_failure(monkeypatch, tmp_path):
    calls = []

    class _BindLogger:
        def warning(self, msg):
            calls.append(msg)

    class _Logger:
        def bind(self, **_kwargs):
            return _BindLogger()

    def _walk_with_error(_repo_root, topdown=True, onerror=None):
        if onerror is not None:
            onerror(OSError("boom"))
        return []

    monkeypatch.setattr(repo_scanner, "logger", _Logger())
    monkeypatch.setattr(repo_scanner.os, "walk", _walk_with_error)

    docs = repo_scanner.scan_repository_docs(tmp_path)

    assert docs == []
    assert "Repository traversal failure" in calls


def test_discover_repository_docs_generates_metadata_and_filters_invalid(tmp_path):
    _touch(tmp_path / "README.md", "# intro")
    _touch(tmp_path / "docs" / "api.md", "# api")
    _touch(tmp_path / "docs" / "empty.md", "   ")
    _touch(tmp_path / "docs" / "spec.pdf", "binary")
    _touch(tmp_path / "images" / "logo.png", "binary")

    docs = repo_scanner.discover_repository_docs(tmp_path, "ad/repo")

    assert docs == [
        {
            "repo_id": "ad/repo",
            "doc_path": "README.md",
            "doc_type": "readme",
            "file_name": "README.md",
            "relative_path": "",
            "file_extension": ".md",
        },
        {
            "repo_id": "ad/repo",
            "doc_path": "docs/api.md",
            "doc_type": "docs",
            "file_name": "api.md",
            "relative_path": "docs",
            "file_extension": ".md",
        },
    ]


def test_discover_repository_docs_skips_unreadable_markdown(monkeypatch, tmp_path):
    _touch(tmp_path / "README.md", "# intro")

    original_read_text = Path.read_text

    def _fake_read_text(self, *args, **kwargs):
        if self.name == "README.md":
            raise OSError("permission denied")
        return original_read_text(self, *args, **kwargs)

    monkeypatch.setattr(Path, "read_text", _fake_read_text)

    docs = repo_scanner.discover_repository_docs(tmp_path, "ad/repo")

    assert docs == []
