"""SQLite parser plugin for MacOS TCC database files."""

from dfdatetime import posix_time as dfdatetime_posix_time

from plaso.containers import events
from plaso.parsers import sqlite
from plaso.parsers.sqlite_plugins import interface


class MacOSTCCEntry(events.EventData):
    """MacOS TCC event data.

    Attributes:
      allowed (bool): whether access to the service was allowed.
      client (str): name of the client requesting access to the service.
      modification_time (dfdatetime.DateTimeValues): date and time of the entry
          last modification.
      prompt_count (int): number of times an application prompted the user for
          access to a service.
      query (str): SQL query that was used to obtain the event data.
      service (str): name of the service.
    """

    DATA_TYPE = "macos:tcc_entry"

    def __init__(self):
        """Initializes event data."""
        super().__init__(data_type=self.DATA_TYPE)
        self.allowed = None
        self.client = None
        self.modification_time = None
        self.prompt_count = None
        self.query = None
        self.service = None


class MacOSTCCPlugin(interface.SQLitePlugin):
    """SQLite parser plugin for MacOS TCC database files.

    The MacOS Transparency, Consent, Control (TCC) database file is typically
    stored in:
    /Library/Application Support/com.apple.TCC/TCC.db
    /Users/<username>/Library/Application Support/com.apple.TCC/TCC.db
    """

    NAME = "macostcc"
    DATA_FORMAT = (
        "MacOS Transparency, Consent, Control (TCC) SQLite database (TCC.db) file"
    )

    REQUIRED_STRUCTURE = {
        "access": frozenset(
            ["allowed", "client", "last_modified", "prompt_count", "service"]
        ),
        "access_overrides": frozenset([]),
        "active_policy": frozenset([]),
        "admin": frozenset([]),
        "expired": frozenset([]),
        "policies": frozenset([]),
    }

    QUERIES = [
        (
            (
                "SELECT service, client, allowed, prompt_count, last_modified "
                "FROM access;"
            ),
            "ParseTCCEntry",
        )
    ]

    SCHEMAS = [
        {
            "access": (
                "CREATE TABLE access ( service TEXT NOT NULL, client TEXT NOT NULL, "
                "client_type INTEGER NOT NULL, allowed INTEGER NOT NULL, "
                "prompt_count INTEGER NOT NULL, csreq BLOB, policy_id INTEGER, "
                "indirect_object_identifier_type INTEGER, "
                "indirect_object_identifier TEXT, indirect_object_code_identity "
                "BLOB, flags INTEGER, last_modified INTEGER NOT NULL DEFAULT "
                "(CAST(strftime('%s','now') AS INTEGER)), PRIMARY KEY (service, "
                "client, client_type, indirect_object_identifier), FOREIGN KEY "
                "(policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE "
                "CASCADE)"
            ),
            "access_overrides": (
                "CREATE TABLE access_overrides ( service TEXT NOT NULL PRIMARY KEY)"
            ),
            "active_policy": (
                "CREATE TABLE active_policy ( client TEXT NOT NULL, client_type "
                "INTEGER NOT NULL, policy_id INTEGER NOT NULL, PRIMARY KEY (client, "
                "client_type), FOREIGN KEY (policy_id) REFERENCES policies(id) ON "
                "DELETE CASCADE ON UPDATE CASCADE)"
            ),
            "admin": (
                "CREATE TABLE admin (key TEXT PRIMARY KEY NOT NULL, value INTEGER "
                "NOT NULL)"
            ),
            "expired": (
                "CREATE TABLE expired ( service TEXT NOT NULL, client TEXT NOT "
                "NULL, client_type INTEGER NOT NULL, csreq BLOB, last_modified "
                "INTEGER NOT NULL , expired_at INTEGER NOT NULL DEFAULT "
                "(CAST(strftime('%s','now') AS INTEGER)), PRIMARY KEY (service, "
                "client, client_type))"
            ),
            "policies": (
                "CREATE TABLE policies ( id INTEGER NOT NULL PRIMARY KEY, bundle_id "
                "TEXT NOT NULL, uuid TEXT NOT NULL, display TEXT NOT NULL, UNIQUE "
                "(bundle_id, uuid))"
            ),
        }
    ]

    def _GetDateTimeRowValue(self, query_hash, row, value_name):
        """Retrieves a date and time value from the row.

        Args:
          query_hash (int): hash of the query, that uniquely identifies the query
              that produced the row.
          row (sqlite3.Row): row.
          value_name (str): name of the value.

        Returns:
          dfdatetime.PosixTime: date and time value or None if not available.
        """
        timestamp = self._GetRowValue(query_hash, row, value_name)
        if timestamp is None:
            return None

        return dfdatetime_posix_time.PosixTime(timestamp=timestamp)

    def ParseTCCEntry(self, parser_mediator, query, row, **unused_kwargs):
        """Parses an application usage row.

        Args:
          parser_mediator (ParserMediator): mediates interactions between parsers
              and other components, such as storage and dfVFS.
          query (str): query that created the row.
          row (sqlite3.Row): row.
        """
        query_hash = hash(query)

        event_data = MacOSTCCEntry()
        event_data.allowed = self._GetRowValue(query_hash, row, "allowed")
        event_data.client = self._GetRowValue(query_hash, row, "client")
        event_data.modification_time = self._GetDateTimeRowValue(
            query_hash, row, "last_modified"
        )
        event_data.prompt_count = self._GetRowValue(query_hash, row, "prompt_count")
        event_data.query = query
        event_data.service = self._GetRowValue(query_hash, row, "service")

        parser_mediator.ProduceEventData(event_data)


sqlite.SQLiteParser.RegisterPlugin(MacOSTCCPlugin)
