import os
import subprocess
import json
import sys
from pathlib import Path

DEFAULT_IGNORED_DIRECTORIES = {
    "venv", ".venv", "env", ".env", "__pycache__", "node_modules", 
    "dist", "build", ".git", ".mypy_cache", ".pytest_cache", ".tox", 
    "site-packages", "__MACOSX"
}

ALLOWED_FILE_TYPES = {".py"}

class Scanner:
    def __init__(self, target_path: Path):
        self.target_path = target_path
        self.scanned_files = []
        self._discover_files()

    def _parse_gitignore(self) -> set:
        gitignore_path = self.target_path / ".gitignore"
        ignore_list = set(DEFAULT_IGNORED_DIRECTORIES)
        if gitignore_path.exists() and gitignore_path.is_file():
            with open(gitignore_path, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith("#"):
                        # Basic .gitignore parsing for directory names
                        clean_line = line.rstrip("/")
                        if clean_line:
                            ignore_list.add(clean_line)
        return ignore_list

    def _discover_files(self):
        ignored_directories = self._parse_gitignore()
        collected_files = []

        for root, dirs, files in os.walk(self.target_path):
            # Filter directories in-place to prevent os.walk from descending into them
            dirs[:] = [d for d in dirs if d not in ignored_directories]

            for file in files:
                if any(file.endswith(ext) for ext in ALLOWED_FILE_TYPES):
                    file_path = Path(root) / file
                    collected_files.append(str(file_path))

        self.scanned_files = collected_files

    def has_python_files(self) -> bool:
        return len(self.scanned_files) > 0

    def _validate_safety(self):
        file_count = len(self.scanned_files)
        print(f"Total scanned files: {file_count}", file=sys.stderr)
        
        if file_count > 500:
            print("Warning: Large scan detected. Ensure venv is excluded.", file=sys.stderr)

        for f in self.scanned_files:
            # Safety validation rule
            if "venv/" in f or ".venv/" in f or "site-packages/" in f:
                print(f"Internal Warning: Excluded path pattern detected in {f}", file=sys.stderr)

    def run_scan(self) -> dict:
        self._validate_safety()
        
        if not self.scanned_files:
            return {"bandit": {"results": []}, "radon": {}}

        results = {
            "bandit": self._run_bandit(),
            "radon": self._run_radon_cc()
        }
        return results

    def _run_bandit(self) -> dict:
        try:
            # We pass the discovered files directly ensuring no ignored path is scanned
            cmd = ["bandit", "-f", "json", "-q"] + self.scanned_files
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=False
            )
            if result.stdout.strip():
                return json.loads(result.stdout)
            return {"results": []}
        except Exception as e:
            print(f"Warning: Bandit scanner failed: {e}", file=sys.stderr)
            return {"results": []}

    def _run_radon_cc(self) -> dict:
        try:
            cmd = ["radon", "cc", "-j"] + self.scanned_files
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=False
            )
            if result.stdout.strip():
                return json.loads(result.stdout)
            return {}
        except Exception as e:
            print(f"Warning: Radon scanner failed: {e}", file=sys.stderr)
            return {}
