"""
Common file filtering logic for RAG and Audit Agents.
"""

import os
import fnmatch
from typing import List, Set, Optional

# Supported text file extensions (Code and Config)
TEXT_EXTENSIONS = {
    # Core Languages
    ".py", ".js", ".ts", ".tsx", ".jsx", ".java", ".go", ".rs",
    ".cpp", ".c", ".h", ".cc", ".hh", ".hpp", ".hxx", ".cs", ".php", ".rb",
    ".kt", ".swift", ".dart", ".scala", ".sc", ".groovy", ".ktm", ".kts",
    # .NET (Logic code)
    ".cshtml", ".vb", ".fs", ".fsi", ".fsx", ".asax", ".master", ".ascx", ".asmx", ".svc",
    # Data & Config
    ".json", ".yml", ".yaml", ".toml", ".xml", ".properties", ".conf", ".ini",
    # Scripts & Commands
    ".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat", ".cmd", ".sql", ".pl", ".pm", ".t",
    # Web
    ".html", ".css", ".vue", ".svelte", ".md", ".proto", ".graphql", ".gql",
    ".prisma", ".sol", ".dockerfile", ".tf", ".hcl", ".tfvars",
    # Others
    ".lua", ".hs", ".lhs", ".clj", ".cljs", ".cljc", ".edn", ".ex", ".exs",
    ".erl", ".hrl", ".m", ".mm", ".r", ".rmd"
}

# Directories to exclude
EXCLUDE_DIRS = {
    # Build & Dependencies
    "node_modules", "vendor", "dist", "build", "target", "out", "bin", "obj",
    "bower_components", "packages", "pkg", "Pods", ".gradle", ".m2",
    "vendor/bundle", ".bundle", "jspm_packages", "typings",
    # Virtual Environments
    "venv", "env", ".env", "virtualenv", ".venv",
    # IDE & Metadata
    ".git", ".svn", ".hg", ".vscode", ".idea", ".vs", "TestResults",
    "_ReSharper.*", ".settings", ".project", ".classpath", ".metadata",
    # Cache & Logs
    "__pycache__", ".pytest_cache", "coverage", "htmlcov", ".nyc_output",
    ".cache", ".next", ".nuxt", ".dart_tool", "htmlcov", "logs", "ipch",
    # Cloud & Infrastructure
    ".aws-sam", ".serverless", ".terraform", ".terraform.d", "_site",
    # Others
    "__MACOSX", "extern", "externals", "third-party", "3rdparty"
}

# Files to exclude (supports glob patterns)
EXCLUDE_FILES = {
    # Lock files
    "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "Cargo.lock",
    "poetry.lock", "composer.lock", "Gemfile.lock", "gradle.lockfile",
    "mix.lock", "pnpm-workspace.yaml", "shrinkwrap.yaml",
    # Static & Binary
    "*.min.js", "*.min.css", "jquery.js", "jquery.min.js", "*.map",
    "*.pyc", "*.pyo", "*.pyd", "*.so", "*.dll", "*.exe", "*.o", "*.obj",
    "*.a", "*.lib", "*.jar", "*.war", "*.ear", "*.class",
    "*.svg", "*.ico", "*.woff*", "*.png", "*.jpg", "*.jpeg", "*.gif",
    # System & Secrets
    ".DS_Store", "thumbs.db", "desktop.ini", "*.pem", "*.crt", "*.key",
    # Temp & Logs
    "*.log", "*.bak", "*.swp", "*.tmp", "tags",
    # IDE & Project Config (non-code)
    "*.suo", "*.user", "*.sln", "*.csproj", "*.vbproj", "*.fsproj", 
    "*.props", "*.targets", "*.resx", "*.sln.docstates", "*.vshost.*", "*.pdb",
    "launchSettings.json", "dotnet-tools.json", ".ruby-version", ".nvmrc",
    # Generated code
    "*.Designer.cs", "*.Designer.vb", "*ModelSnapshot.cs", "*.generated.cs", "*.g.cs", "*.g.i.cs",
    # Large data files
    "haarcascade_*.xml"
}

def is_text_file(file_path: str) -> bool:
    """Check if a file should be considered for indexing/scanning."""
    ext = os.path.splitext(file_path)[1].lower()
    return ext in TEXT_EXTENSIONS

def should_exclude(rel_path: str, filename: str, exclude_patterns: Optional[List[str]] = None) -> bool:
    """
    Check if a file should be excluded based on default rules and custom patterns.
    """
    # 1. Check EXCLUDE_FILES (glob patterns)
    for pattern in EXCLUDE_FILES:
        if fnmatch.fnmatch(filename, pattern):
            return True
            
    # 2. Check EXCLUDE_DIRS in the path
    path_parts = rel_path.replace('\\', '/').split('/')
    for part in path_parts[:-1]: # Don't check filename as a directory
        if part in EXCLUDE_DIRS:
            return True
            
    # 3. Check custom exclude_patterns
    if exclude_patterns:
        for pattern in exclude_patterns:
            if fnmatch.fnmatch(rel_path, pattern) or fnmatch.fnmatch(filename, pattern):
                return True
                
    return False
