"""Gitignore-aware file ignore logic.

Consolidates ignore pattern handling used across sync, watch, and reindex commands.
Uses pathspec for full .gitignore spec support (gitwildmatch).
"""

from pathlib import Path
from typing import Callable

import pathspec

DEFAULT_IGNORE_PATTERNS = [
    ".git/",
    "__pycache__/",
    "*.pyc",
    ".venv/",
    "venv/",
    "env/",
    "node_modules/",
    ".nogic/",
    ".DS_Store",
    "*.swp",
    "*.swo",
    "*.tmp.*",
    "*~",
    # Build output directories
    "dist/",
    "build/",
    "out/",
    ".next/",
    ".nuxt/",
    ".output/",
    # Coverage and test output
    "coverage/",
    ".coverage",
    "htmlcov/",
    # IDE and editor files
    ".idea/",
    ".vscode/",
    # Bundled/minified files
    "*.min.js",
    "*.min.css",
    "*.bundle.js",
    "*.chunk.js",
    # Lock files and binary artifacts
    "*.lock",
    "package-lock.json",
    "yarn.lock",
    "pnpm-lock.yaml",
    "*.map",
]


def load_gitignore_patterns(root: Path) -> list[str]:
    """Read .gitignore from root and return pattern lines.

    Skips blank lines and comments. Returns empty list if no .gitignore exists.
    """
    gitignore_path = root / ".gitignore"
    if not gitignore_path.is_file():
        return []

    try:
        text = gitignore_path.read_text(encoding="utf-8")
    except (OSError, UnicodeDecodeError):
        return []

    patterns = []
    for line in text.splitlines():
        stripped = line.strip()
        if stripped and not stripped.startswith("#"):
            patterns.append(stripped)
    return patterns


def build_ignore_matcher(
    root: Path,
    extra_patterns: tuple[str, ...] | list[str] = (),
) -> Callable[[Path], bool]:
    """Build an ignore checker combining defaults + .gitignore + extra patterns.

    Returns a callable that takes an absolute Path and returns True if it should
    be ignored. Uses pathspec with gitwildmatch for full .gitignore compatibility.
    """
    root = root.resolve()

    all_patterns = (
        list(DEFAULT_IGNORE_PATTERNS)
        + load_gitignore_patterns(root)
        + list(extra_patterns)
    )

    spec = pathspec.PathSpec.from_lines("gitignore", all_patterns)

    def should_ignore(path: Path) -> bool:
        try:
            rel_path = path.relative_to(root)
        except ValueError:
            return True

        rel_str = str(rel_path)
        # pathspec needs forward slashes; on Windows Path uses backslashes
        rel_str = rel_str.replace("\\", "/")
        # Append trailing slash for directories so dir patterns match
        if path.is_dir():
            rel_str += "/"
        return spec.match_file(rel_str)

    return should_ignore
