from __future__ import annotations

import logging
from pathlib import PurePosixPath

_CACHE_DIR_NAMES = {"__pycache__", "app-data", "build", "grafana"}
LOGGER = logging.getLogger(__name__)


def should_skip_indexing_path(path: str) -> bool:
    return indexing_exclusion_reason(path) is not None


def indexing_exclusion_reason(path: str) -> str | None:
    parts = _path_parts(path)
    if not parts:
        return "empty_path"
    for part in parts:
        if part in _CACHE_DIR_NAMES:
            return f"cache_dir:{part}"
        if _is_hidden_part(part):
            return f"hidden_part:{part}"
    return None


def filter_snapshot_files(files: list[dict]) -> list[dict]:
    out: list[dict] = []
    for item in files:
        path = str(item.get("path", ""))
        reason = indexing_exclusion_reason(path)
        if reason is not None:
            LOGGER.warning("rag ingest excluded file: path=%s reason=%s", path, reason)
            continue
        out.append(item)
    return out


def filter_changes_for_indexing(changed_files: list[dict]) -> list[dict]:
    result: list[dict] = []
    for item in changed_files:
        op = str(item.get("op", "")).lower()
        path = str(item.get("path", ""))
        if op == "delete":
            result.append(item)
            continue
        reason = indexing_exclusion_reason(path)
        if reason is not None:
            LOGGER.warning(
                "rag ingest excluded changed file: op=%s path=%s reason=%s",
                op,
                path,
                reason,
            )
            continue
        result.append(item)
    return result


def count_indexable_change_upserts(changed_files: list[dict]) -> int:
    total = 0
    for item in changed_files:
        op = str(item.get("op", "")).lower()
        if op == "delete":
            continue
        path = str(item.get("path", ""))
        if should_skip_indexing_path(path):
            continue
        total += 1
    return total


def _path_parts(path: str) -> tuple[str, ...]:
    normalized = (path or "").replace("\\", "/").strip()
    if not normalized:
        return ()
    posix = PurePosixPath(normalized)
    return tuple(part for part in posix.parts if part not in {"", "/"} and part != ".")


def _is_hidden_part(part: str) -> bool:
    return part not in {".", ".."} and part.startswith(".")
