"""Path filters.

Path filters are specified in filter files and are used during collection to
include or exclude file system paths.
"""

from dfvfs.helpers import file_system_searcher

from plaso.engine import logger
from plaso.engine import path_helper


class PathFilter:
  """Path filter.

  Attributes:
    description (str): description of the purpose of the filter or None if
        not set.
    filter_type (str): indicates if the filter should include or excludes
        paths during collection.
    path_separator (str): path segment separator.
    paths (list[str]): paths to filter.
  """

  FILTER_TYPE_EXCLUDE = 'exclude'
  FILTER_TYPE_INCLUDE = 'include'

  def __init__(
      self, filter_type, description=None, path_separator='/', paths=None):
    """Initializes a path filter.

    Args:
      filter_type (str): indicates if the filter should include or excludes
          paths during collection.
      description (Optional[str]): description of the purpose of the filter.
      path_separator (Optional[str]): path segment separator.
      paths (Optional[list[str]]): paths to filter.

    Raises:
      ValueError: if the filter type contains an unsupported value.
    """
    if filter_type not in (self.FILTER_TYPE_EXCLUDE, self.FILTER_TYPE_INCLUDE):
      raise ValueError(f'Unsupported filter type: {filter_type!s}')

    super().__init__()
    self.description = description
    self.filter_type = filter_type
    self.path_separator = path_separator
    self.paths = paths or []


class PathCollectionFiltersHelper:
  """Path collection filters helper.

  Attributes:
    excluded_file_system_find_specs (list[dfvfs.FindSpec]): file system find
        specifications of paths to exclude from the collection.
    included_file_system_find_specs (list[dfvfs.FindSpec]): file system find
        specifications of paths to include in the collection.
  """

  def __init__(self):
    """Initializes a collection filters helper."""
    super().__init__()
    self.excluded_file_system_find_specs = []
    self.included_file_system_find_specs = []

  def BuildFindSpecs(self, path_filters, environment_variables=None):
    """Builds find specifications from path filters.

    Args:
      path_filters (list[PathFilter]): path filters.
      environment_variables (Optional[list[EnvironmentVariableArtifact]]):
          environment variables.
    """
    for path_filter in path_filters:
      for path in path_filter.paths:
        # Since paths are regular expression the path separator is escaped.
        if path_filter.path_separator == '\\':
          path_separator = '\\\\'
        else:
          path_separator = path_filter.path_separator

        expand_path = False
        path_segments = path.split(path_separator)
        for index, path_segment in enumerate(path_segments):
          if len(path_segment) <= 2:
            continue

          if path_segment[0] == '{' and path_segment[-1] == '}':
            # Rewrite legacy path expansion attributes, such as {systemroot}
            # into %SystemRoot%.
            path_segment = f'%{path_segment[1:-1]:s}%'
            path_segments[index] = path_segment

          if path_segment[0] == '%' and path_segment[-1] == '%':
            expand_path = True

        if expand_path:
          path_segments = path_helper.PathHelper.ExpandWindowsPathSegments(
              path_segments, environment_variables)

        if path_segments[0] != '':
          logger.warning(
              f'The path filter must be defined as an absolute path: {path:s}')
          continue

        # Strip the root path segment.
        path_segments.pop(0)

        if not path_segments[-1]:
          logger.warning(f'Empty last path segment in path: {path:s}')
          continue

        find_spec = file_system_searcher.FindSpec(
            case_sensitive=False, location_regex=path_segments)

        if path_filter.filter_type == PathFilter.FILTER_TYPE_EXCLUDE:
          self.excluded_file_system_find_specs.append(find_spec)

        elif path_filter.filter_type == PathFilter.FILTER_TYPE_INCLUDE:
          self.included_file_system_find_specs.append(find_spec)
