# constants.py — media-type data, default allow/exclude sets, and the
# canonical-kwargs alias map used to normalize caller keyword arguments.
import re
from typing import Callable, List, Optional, Set, Dict

# -------------------------
# Media types
# -------------------------
# Category -> set of extensions.
MEDIA_TYPES: Dict[str, Set[str]] = {
    "archive": {".7z", ".bz2", ".dmg", ".egg", ".gz", ".iso", ".jar", ".rar",
                ".tar", ".tgz", ".war", ".whl", ".xz", ".zip", ".zpaq"},
    "audio": {".aac", ".aif", ".aiff", ".amr", ".flac", ".m4a", ".mid", ".midi",
              ".mka", ".mp3", ".ogg", ".opus", ".wav", ".wma"},
    "code": {".bash", ".c", ".cjs", ".coffee", ".cpp", ".css", ".dockerfile",
             ".go", ".gql", ".graphql", ".h", ".hpp", ".htm", ".html", ".java",
             ".js", ".jsx", ".kt", ".less", ".lua", ".makefile", ".mjs", ".php",
             ".ps1", ".py", ".rb", ".rs", ".sass", ".scss", ".sh", ".sol",
             ".sql", ".svelte", ".swift", ".ts", ".tsx", ".vue", ".yml"},
    "data_science": {".avro", ".db", ".h5", ".hdf5", ".ipynb", ".npy", ".npz",
                     ".parquet", ".pickle", ".sqlite"},
    "document": {".azw", ".cfg", ".csv", ".doc", ".docx", ".epub", ".ini",
                 ".json", ".key", ".log", ".markdown", ".md", ".mobi",
                 ".numbers", ".odt", ".pages", ".pdf", ".rtf", ".tex", ".toml",
                 ".tsv", ".txt", ".xml", ".yaml", ".yml"},
    "executable": {".apk", ".app", ".bin", ".deb", ".dll", ".exe", ".ipa", ".rpm"},
    "font": {".eot", ".otf", ".ttf", ".woff", ".woff2"},
    "geospatial": {".dbf", ".geojson", ".gpkg", ".gpx", ".kml", ".kmz", ".osm",
                   ".shp", ".shx", ".tif", ".tiff", ".wkt"},
    "image": {".ai", ".apng", ".bmp", ".eps", ".gif", ".heic", ".heif", ".ico",
              ".jp2", ".jpeg", ".jpg", ".jxl", ".png", ".psd", ".raw", ".svg",
              ".tiff", ".webp"},
    "pandas_data": {".dta", ".feather", ".h5", ".hdf", ".msgpack", ".orc",
                    ".parquet", ".pickle", ".pkl", ".sas7bdat", ".sav", ".stata"},
    "presentation": {".odp", ".ppt", ".pptx"},
    "spreadsheet": {".csv", ".ods", ".tsv", ".xls", ".xlsx"},
    "three_d": {".blend", ".fbx", ".glb", ".gltf", ".obj", ".stl", ".usd", ".usdz"},
    "video": {".3gp", ".asf", ".avi", ".f4v", ".flv", ".m2ts", ".m4v", ".mkv",
              ".mov", ".mp4", ".mpeg", ".mpg", ".mts", ".ogg", ".vob", ".webm",
              ".wmv"},
}

def get_media_map(categories=None):
    """
    Return a sub-dict of MEDIA_TYPES for the given categories.
    If categories is None or empty, return the whole MEDIA_TYPES.
    """
    if not categories:
        return MEDIA_TYPES
    cats = {str(c) for c in categories}
    return {c: MEDIA_TYPES[c] for c in cats if c in MEDIA_TYPES}

def get_media_exts(categories=None):
    """
    Return a flat, sorted list of all extensions for the given categories.
    """
    media_map = get_media_map(categories)
    return sorted({ext for exts in media_map.values() for ext in exts})

def derive_media_type(obj):
    ext = None
    if obj:
        import os
        ext = os.path.splitext(str(obj))[-1] or str(obj)
    if ext:
        for typ, exts in MEDIA_TYPES.items():
            if ext in exts:
                return typ

# -------------------------
# Default sets
# -------------------------
DEFAULT_ALLOWED_EXTS: Set[str] = {
    ".py", ".pyw",                             # python
    ".js", ".jsx", ".ts", ".tsx", ".mjs",      # JS/TS
    ".html", ".htm", ".xml",                   # markup
    ".css", ".scss", ".sass", ".less",         # styles
    ".json", ".yaml", ".yml", ".toml", ".ini",  # configs
    ".cfg", ".md", ".markdown", ".rst",        # docs
    ".sh", ".bash", ".env",                    # scripts/env
    ".txt"                                     # plain text
}

DEFAULT_EXCLUDE_TYPES: Set[str] = {
    "image", "video", "audio", "presentation",
    "spreadsheet", "archive", "executable"
}

# never want these—even if they sneak into ALLOWED
_unallowed = set(get_media_exts(DEFAULT_EXCLUDE_TYPES)) | {
    ".bak", ".shp", ".cpg", ".dbf", ".shx", ".geojson",
    ".pyc", ".prj", ".sbn", ".sbx"
}
DEFAULT_EXCLUDE_EXTS: Set[str] = {e for e in _unallowed if e not in DEFAULT_ALLOWED_EXTS}

DEFAULT_EXCLUDE_DIRS: Set[str] = {
    "node_modules", "old", "__pycache__", "backups", "backup",
    "backs", "trash", "depriciated", "__init__"
}

DEFAULT_EXCLUDE_PATTERNS: Set[str] = {
    "__init__*", "*.tmp", "*.log", "*.lock", "*.zip", "*~"
}
DEFAULT_EXCLUDE_FILE_PATTERNS = DEFAULT_EXCLUDE_PATTERNS
DEFAULT_ALLOWED_PATTERNS: List[str] = ["*"]
DEFAULT_ALLOWED_DIRS: List[str] = ["*"]
DEFAULT_ALLOWED_TYPES: List[str] = ["*"]

AllowedPredicate = Optional[Callable[[str], bool]]

# -------------------------
# Canonical kwargs aliases
# -------------------------
CANONICAL_MAP = {
    "directories": ["directory", "directories", "dir", "dirs", "d", "dirname", "paths", "path", "roots", "root"],
    "files": ["file", "filepath", "file_path", "files", "filepaths", "file_paths", "paths", "path", "f"],
    "allowed_exts": ["allow_ext", "allowed_ext", "include_ext", "include_exts", "exts_allowed"],
    "exclude_exts": ["exclude_ext", "excluded_ext", "excluded_exts", "unallowed_ext", "unallowed_exts"],
    "allowed_types": ["allow_type", "allowed_type", "include_type", "include_types", "types_allowed"],
    "exclude_types": ["exclude_type", "excluded_type", "excluded_types", "unallowed_type", "unallowed_types"],
    "allowed_dirs": ["allow_dir", "allowed_dir", "include_dir", "include_dirs", "dirs_allowed"],
    "exclude_dirs": ["exclude_dir", "excluded_dir", "excluded_dirs", "unallowed_dir", "unallowed_dirs"],
    "allowed_patterns": ["allow_pattern", "allowed_pattern", "include_pattern", "include_patterns", "patterns_allowed"],
    "exclude_patterns": ["exclude_pattern", "excluded_pattern", "excluded_patterns", "unallowed_pattern", "unallowed_patterns"],
    "add": ["add"],
    "cfg": ["cfg"],
    "recursive": ["recursive"],
    "strings": ["strings"],
    "total_strings": ["total_strings"],
    "parse_lines": ["parse_lines"],
    "spec_line": ["spec_line"],
    "get_lines": ["get_lines"],
}
DEFAULT_ALLOWED_EXCLUDE_MAP = {
    "allowed_exts": {"default": DEFAULT_ALLOWED_EXTS, "type": type(DEFAULT_ALLOWED_EXTS), "canonical": CANONICAL_MAP.get("allowed_exts")},
    "exclude_exts": {"default": DEFAULT_EXCLUDE_EXTS, "type": type(DEFAULT_EXCLUDE_EXTS), "canonical": CANONICAL_MAP.get("exclude_exts")},
    "allowed_types": {"default": DEFAULT_ALLOWED_TYPES, "type": type(DEFAULT_ALLOWED_TYPES), "canonical": CANONICAL_MAP.get("allowed_types")},
    "exclude_types": {"default": DEFAULT_EXCLUDE_TYPES, "type": type(DEFAULT_EXCLUDE_TYPES), "canonical": CANONICAL_MAP.get("exclude_types")},
    "allowed_dirs": {"default": DEFAULT_ALLOWED_DIRS, "type": type(DEFAULT_ALLOWED_DIRS), "canonical": CANONICAL_MAP.get("allowed_dirs")},
    "exclude_dirs": {"default": DEFAULT_EXCLUDE_DIRS, "type": type(DEFAULT_EXCLUDE_DIRS), "canonical": CANONICAL_MAP.get("exclude_dirs")},
    "allowed_patterns": {"default": DEFAULT_ALLOWED_PATTERNS, "type": type(DEFAULT_ALLOWED_PATTERNS), "canonical": CANONICAL_MAP.get("allowed_patterns")},
    "exclude_patterns": {"default": DEFAULT_EXCLUDE_PATTERNS, "type": type(DEFAULT_EXCLUDE_PATTERNS), "canonical": CANONICAL_MAP.get("exclude_patterns")},
}
DEFAULT_CANONICAL_MAP = {
    "directories": {"default": [], "type": list, "canonical": CANONICAL_MAP.get("directories")},
    "files": {"default": [], "type": list, "canonical": CANONICAL_MAP.get("files")},
    **DEFAULT_ALLOWED_EXCLUDE_MAP,
    "allowed": {"default": None, "type": bool, "canonical": CANONICAL_MAP.get("allowed")},
    "add": {"default": False, "type": bool, "canonical": CANONICAL_MAP.get("add")},
    "recursive": {"default": True, "type": bool, "canonical": CANONICAL_MAP.get("recursive")},
    "strings": {"default": None, "type": list, "canonical": CANONICAL_MAP.get("strings")},
    "total_strings": {"default": False, "type": bool, "canonical": CANONICAL_MAP.get("total_strings")},
    "parse_lines": {"default": False, "type": bool, "canonical": CANONICAL_MAP.get("parse_lines")},
    "spec_line": {"default": False, "type": bool, "canonical": CANONICAL_MAP.get("spec_line")},
    "get_lines": {"default": False, "type": bool, "canonical": CANONICAL_MAP.get("get_lines")},
}
