"""
This module provides :class:`.GitIgnoreSpec` which replicates
*.gitignore* behavior.
"""

from typing import AnyStr, Callable, Collection, Iterable, TYPE_CHECKING, Union

from .pathspec import PathSpec
from .pattern import Pattern
from .patterns.gitwildmatch import (
    GitWildMatchPattern,
    GitWildMatchPatternError,
    _DIR_MARK,
)
from .util import _is_iterable


class GitIgnoreSpec(PathSpec):
    """
    The :class:`GitIgnoreSpec` class extends :class:`PathSpec` to
    replicate *.gitignore* behavior.
    """

    def __eq__(self, other: "Self") -> bool:
        """
        Tests the equality of this gitignore-spec with *other*
        (:class:`GitIgnoreSpec`) by comparing their :attr:`~PathSpec.patterns`
        attributes. A non-:class:`GitIgnoreSpec` will not compare equal.
        """
        if isinstance(other, GitIgnoreSpec):
            return super().__eq__(other)
        elif isinstance(other, PathSpec):
            return False
        else:
            return NotImplemented

    @classmethod
    def from_lines(
        cls,
        lines: Iterable[AnyStr],
        pattern_factory: Union[str, Callable[[AnyStr], Pattern], None] = None,
    ) -> "Self":
        """
        Compiles the pattern lines.

        *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled
        pattern (:class:`str`). This simply has to yield each line so it can
        be a :class:`io.TextIOBase` (e.g., from :func:`open` or
        :class:`io.StringIO`) or the result from :meth:`str.splitlines`.

        *pattern_factory* can be :data:`None`, the name of a registered
        pattern factory (:class:`str`), or a :class:`~collections.abc.Callable`
        used to compile patterns. The callable must accept an uncompiled
        pattern (:class:`str`) and return the compiled pattern (:class:`.Pattern`).
        Default is :data:`None` for :class:`.GitWildMatchPattern`).

        Returns the :class:`GitIgnoreSpec` instance.
        """
        if pattern_factory is None:
            pattern_factory = GitWildMatchPattern

        elif (isinstance(lines, str) or callable(lines)) and _is_iterable(
            pattern_factory
        ):
            # Support reversed order of arguments from PathSpec.
            pattern_factory, lines = lines, pattern_factory

        self = super().from_lines(pattern_factory, lines)
        return self  # type: ignore

    @staticmethod
    def _match_file(
        patterns: Collection[GitWildMatchPattern],
        file: str,
    ) -> bool:
        """
        Matches the file to the patterns.

        .. NOTE:: Subclasses of :class:`.PathSpec` may override this
           method as an instance method. It does not have to be a static
           method.

        *patterns* (:class:`~collections.abc.Iterable` of :class:`~pathspec.pattern.Pattern`)
        contains the patterns to use.

        *file* (:class:`str`) is the normalized file path to be matched
        against *patterns*.

        Returns :data:`True` if *file* matched; otherwise, :data:`False`.
        """
        out_matched = False
        out_priority = 0
        for pattern in patterns:
            if pattern.include is not None:
                match = pattern.match_file(file)
                if match is not None:
                    # Pattern matched.

                    # Check for directory marker.
                    try:
                        dir_mark = match.match.group(_DIR_MARK)
                    except IndexError as e:
                        # NOTICE: The exact content of this error message is subject
                        # to change.
                        raise GitWildMatchPatternError(
                            (
                                f"Invalid git pattern: directory marker regex group is missing. "
                                f"Debug: file={file!r} regex={pattern.regex!r} "
                                f"group={_DIR_MARK!r} match={match.match!r}."
                            )
                        ) from e

                    if dir_mark:
                        # Pattern matched by a directory pattern.
                        priority = 1
                    else:
                        # Pattern matched by a file pattern.
                        priority = 2

                    if priority >= out_priority:
                        out_matched = pattern.include
                        out_priority = priority

        return out_matched


if TYPE_CHECKING:
    try:
        from typing import Self
    except ImportError:
        try:
            from typing_extensions import Self
        except ImportError:
            Self = GitIgnoreSpec
