"""Tests for the scan pipeline executor."""

from __future__ import annotations

from pathlib import Path

import pytest

from sanicode.config import SanicodeConfig
from sanicode.scanner.executor import ScanOutput, collect_files, run_scan


class TestCollectFiles:
    """Tests for collect_files()."""

    def test_finds_python_files(self, tmp_path: Path) -> None:
        (tmp_path / "a.py").write_text("x = 1\n", encoding="utf-8")
        (tmp_path / "b.py").write_text("y = 2\n", encoding="utf-8")
        (tmp_path / "readme.md").write_text("# Hi\n", encoding="utf-8")

        cfg = SanicodeConfig()
        files = collect_files(tmp_path, cfg)
        names = [f.name for f in files]
        assert "a.py" in names
        assert "b.py" in names
        assert "readme.md" not in names

    def test_respects_exclude_patterns(self, tmp_path: Path) -> None:
        sub = tmp_path / "vendor"
        sub.mkdir()
        (sub / "lib.py").write_text("pass\n", encoding="utf-8")
        (tmp_path / "main.py").write_text("pass\n", encoding="utf-8")

        cfg = SanicodeConfig()
        cfg.scan.exclude_patterns = ["vendor/**"]
        files = collect_files(tmp_path, cfg)
        names = [f.name for f in files]
        assert "main.py" in names
        assert "lib.py" not in names

    def test_respects_max_file_size(self, tmp_path: Path) -> None:
        small = tmp_path / "small.py"
        small.write_text("x = 1\n", encoding="utf-8")
        big = tmp_path / "big.py"
        big.write_text("x = 1\n" * 100_000, encoding="utf-8")

        cfg = SanicodeConfig()
        cfg.scan.max_file_size_kb = 1  # 1 KB
        files = collect_files(tmp_path, cfg)
        names = [f.name for f in files]
        assert "small.py" in names
        assert "big.py" not in names


class TestRunScan:
    """Tests for run_scan()."""

    def test_detects_findings(self, tmp_path: Path) -> None:
        (tmp_path / "bad.py").write_text("eval(input())\n", encoding="utf-8")
        cfg = SanicodeConfig()
        output = run_scan(tmp_path, cfg)

        assert isinstance(output, ScanOutput)
        assert output.file_count == 1
        assert len(output.enriched_findings) > 0
        assert output.enriched_findings[0].rule_id == "SC001"

    def test_clean_code_no_findings(self, tmp_path: Path) -> None:
        (tmp_path / "clean.py").write_text("x = 1 + 2\n", encoding="utf-8")
        cfg = SanicodeConfig()
        output = run_scan(tmp_path, cfg)

        assert output.file_count == 1
        assert len(output.enriched_findings) == 0

    def test_nonexistent_path_raises(self) -> None:
        cfg = SanicodeConfig()
        with pytest.raises(FileNotFoundError):
            run_scan(Path("/nonexistent/xyz"), cfg)

    def test_parse_errors_collected(self, tmp_path: Path) -> None:
        (tmp_path / "bad_syntax.py").write_text("def (\n", encoding="utf-8")
        (tmp_path / "good.py").write_text("x = 1\n", encoding="utf-8")
        cfg = SanicodeConfig()
        output = run_scan(tmp_path, cfg)

        assert len(output.parse_errors) == 1
        assert "bad_syntax.py" in output.parse_errors[0]
        assert output.file_count == 1  # Only good.py parsed

    def test_graph_data_populated(self, tmp_path: Path) -> None:
        (tmp_path / "app.py").write_text("eval(input())\n", encoding="utf-8")
        cfg = SanicodeConfig()
        output = run_scan(tmp_path, cfg)

        assert isinstance(output.graph_data, dict)
        assert "nodes" in output.graph_data

    def test_scan_result_has_summary(self, tmp_path: Path) -> None:
        (tmp_path / "app.py").write_text("eval(input())\n", encoding="utf-8")
        cfg = SanicodeConfig()
        output = run_scan(tmp_path, cfg)

        assert output.result.summary["total_findings"] > 0
        assert output.result.sanicode_version == "0.1.0"
