"""YAML-based timeliner configuration file."""

import yaml

from plaso.lib import errors


class TimelinerDefinition:
    """Timeliner definition.

    Attributes:
      attribute_mappings (dict[str, str]): date and time description
          (timestamp_desc) per attribute name.
      data_type (str): event data type indicator.
      place_holder_event (bool): True if the timeliner should generate
          a placeholder event if no date and time values were found in
          the event data.
    """

    def __init__(self, data_type):
        """Initializes a timeliner definition.

        Args:
          data_type (str): event data type indicator.
        """
        super().__init__()
        self.attribute_mappings = {}
        self.data_type = data_type
        self.place_holder_event = True


class YAMLTimelinerConfigurationFile:
    """YAML-based timeliner configuration file.

    A YAML-based timeliner configuration file contains one or more timeliner
    definitions. A timeliner definitions consists of:

    data_type: 'fs:stat'
    attribute_mappings:
    - name: 'access_time'
      description: 'Last Access Time'
    place_holder_event: true

    Where:
    * data_type, defines the corresponding event data type;
    * attribute_mappings, defines attribute mappings;
    * place_holder_event, defines if the timeliner should generate a placeholder
      event.
    """

    _SUPPORTED_KEYS = frozenset(
        ["attribute_mappings", "data_type", "place_holder_event"]
    )

    def _ReadTimelinerDefinition(self, timeliner_definition_values):
        """Reads a timeliner definition from a dictionary.

        Args:
          timeliner_definition_values (dict[str, object]): timeliner definition
               values.

        Returns:
          TimelinerDefinition: a timeliner definition.

        Raises:
          ParseError: if the format of the timeliner definition is not set
              or incorrect.
        """
        if not timeliner_definition_values:
            raise errors.ParseError("Missing timeliner definition values.")

        different_keys = set(timeliner_definition_values) - self._SUPPORTED_KEYS
        if different_keys:
            different_keys = ", ".join(different_keys)
            raise errors.ParseError(f"Undefined keys: {different_keys:s}")

        data_type = timeliner_definition_values.get("data_type")
        if not data_type:
            raise errors.ParseError(
                "Invalid event timeliner definition missing data type."
            )

        attribute_mappings = (
            timeliner_definition_values.get("attribute_mappings", None) or []
        )

        timeliner_definition = TimelinerDefinition(data_type)
        timeliner_definition.attribute_mappings = {
            attribute_mapping["name"]: attribute_mapping["description"]
            for attribute_mapping in attribute_mappings
        }
        timeliner_definition.place_holder_event = timeliner_definition_values.get(
            "place_holder_event", True
        )

        return timeliner_definition

    def _ReadFromFileObject(self, file_object):
        """Reads the timeliner definitions from a file-like object.

        Args:
          file_object (file): timeliner definitions file-like object.

        Yields:
          TimelinerDefinition: a timeliner definition.
        """
        yaml_generator = yaml.safe_load_all(file_object)

        for yaml_definition in yaml_generator:
            yield self._ReadTimelinerDefinition(yaml_definition)

    def ReadFromFile(self, path):
        """Reads the timeliner definitions from a YAML file.

        Args:
          path (str): path to a timeliner configuration file.

        Yields:
          TimelinerDefinition: a timeliner definition.
        """
        with open(path, "r", encoding="utf-8") as file_object:
            yield from self._ReadFromFileObject(file_object)
