"""This file contains a plugin for parsing Google Analytics cookies."""

from urllib import parse as urlparse

from plaso.containers import events
from plaso.parsers.cookie_plugins import interface
from plaso.parsers.cookie_plugins import manager

# TODO: determine if __utmc always 0?


class GoogleAnalyticsUtmaEventData(events.EventData):
    """Google analytics __utma cookie event data.

    Attributes:
      cookie_name (str): name of cookie.
      domain_hash (str): domain hash.
      sessions (int): number of sessions.
      url (str): URL or path where the cookie got set.
      visited_times (list[dfdatetime.DateTimeValues]): dates and times the URL
          was visited.
      visitor_identifier (str): visitor identifier.
    """

    DATA_TYPE = "cookie:google:analytics:utma"

    def __init__(self):
        """Initializes event data."""
        super().__init__(data_type=self.DATA_TYPE)
        self.cookie_name = None
        self.domain_hash = None
        self.sessions = None
        self.url = None
        self.visited_times = None
        self.visitor_identifier = None


class GoogleAnalyticsUtmbEventData(events.EventData):
    """Google analytics __utmb cookie event data.

    Attributes:
      cookie_name (str): name of cookie.
      domain_hash (str): domain hash.
      last_visited_time (dfdatetime.DateTimeValues): date and time the URL was
          last visited.
      pages_viewed (int): number of pages viewed.
      url (str): URL or path where the cookie got set.
    """

    DATA_TYPE = "cookie:google:analytics:utmb"

    def __init__(self):
        """Initializes event data."""
        super().__init__(data_type=self.DATA_TYPE)
        self.cookie_name = None
        self.domain_hash = None
        self.last_visited_time = None
        self.pages_viewed = None
        self.url = None


class GoogleAnalyticsUtmtEventData(events.EventData):
    """Google analytics __utmt cookie event data.

    Attributes:
      cookie_name (str): name of cookie.
      last_visited_time (dfdatetime.DateTimeValues): date and time the URL was
          last visited.
      url (str): URL or path where the cookie got set.
    """

    DATA_TYPE = "cookie:google:analytics:utmt"

    def __init__(self):
        """Initializes event data."""
        super().__init__(data_type=self.DATA_TYPE)
        self.cookie_name = None
        self.last_visited_time = None
        self.url = None


class GoogleAnalyticsUtmzEventData(events.EventData):
    """Google analytics __utmz cookie event data.

    Attributes:
      cookie_name (str): name of cookie.
      domain_hash (str): domain hash.
      last_visited_time (dfdatetime.DateTimeValues): date and time the URL was
          last visited.
      sessions (int): number of sessions.
      sources (int): number of sources.
      url (str): URL or path where the cookie got set.
    """

    DATA_TYPE = "cookie:google:analytics:utmz"

    def __init__(self):
        """Initializes event data."""
        super().__init__(data_type=self.DATA_TYPE)
        self.cookie_name = None
        self.domain_hash = None
        self.last_visited_time = None
        self.sessions = None
        self.sources = None
        self.url = None


class GoogleAnalyticsUtmaPlugin(interface.BaseCookiePlugin):
    """A browser cookie plugin for __utma Google Analytics cookies.

    The structure of the cookie data:
    <domain hash>.<visitor ID>.<first visit>.<previous visit>.<last visit>.
    <number of sessions>

    For example:
    137167072.1215918423.1383170166.1383170166.1383170166.1

    Or:
    <last visit>

    For example:
    13113225820000000
    """

    NAME = "google_analytics_utma"
    DATA_FORMAT = "Google Analytics __utma cookie"

    COOKIE_NAME = "__utma"

    def _ParseCookieData(self, parser_mediator, cookie_data=None, url=None, **kwargs):
        """Extracts events from cookie data.

        Args:
          parser_mediator (ParserMediator): mediates interactions between parsers and
              other components, such as storage and dfVFS.
          cookie_data (str): cookie data.
          url (str): URL or path where the cookie got set.
        """
        fields = cookie_data.split(".")
        number_of_fields = len(fields)

        if number_of_fields not in (1, 6):
            parser_mediator.ProduceWarning(
                f"unsupported number of fields: {number_of_fields:d} in cookie: "
                f"{self.COOKIE_NAME:s}"
            )
            return

        visited_times = []

        if number_of_fields == 1:
            domain_hash = None
            visitor_identifier = None
            number_of_sessions = None

            date_time = self._ParsePosixTimeIn100Nanoseconds(fields[0])
            if date_time:
                visited_times.append(date_time)

        elif number_of_fields == 6:
            domain_hash = fields[0]
            visitor_identifier = fields[1]

            # TODO: Double check this time is stored in UTC and not local time.
            date_time = self._ParsePosixTime(fields[2])
            if date_time:
                visited_times.append(date_time)

            date_time = self._ParsePosixTime(fields[3])
            if date_time:
                visited_times.append(date_time)

            date_time = self._ParsePosixTime(fields[4])
            if date_time:
                visited_times.append(date_time)

            number_of_sessions = self._ParseIntegerValue(fields[5])

        else:
            domain_hash = None
            number_of_sessions = None
            visitor_identifier = None

        event_data = GoogleAnalyticsUtmaEventData()
        event_data.cookie_name = self.COOKIE_NAME
        event_data.domain_hash = domain_hash
        event_data.sessions = number_of_sessions
        event_data.url = url
        event_data.visited_times = visited_times or None
        event_data.visitor_identifier = visitor_identifier

        parser_mediator.ProduceEventData(event_data)


class GoogleAnalyticsUtmbPlugin(interface.BaseCookiePlugin):
    """A browser cookie plugin for __utmb Google Analytics cookies.

    The structure of the cookie data:
    <domain hash>.<pages viewed>.<unknown>.<last time>

    For example:
    137167072.1.10.1383170166
    173272373.6.8.1440489514899
    173272373.4.9.1373300660574

    Or:
    <last time>

    For example:
    13113225820000000
    """

    NAME = "google_analytics_utmb"
    DATA_FORMAT = "Google Analytics __utmb cookie"

    COOKIE_NAME = "__utmb"

    def _ParseCookieData(self, parser_mediator, cookie_data=None, url=None, **kwargs):
        """Extracts events from cookie data.

        Args:
          parser_mediator (ParserMediator): mediates interactions between parsers and
              other components, such as storage and dfVFS.
          cookie_data (bytes): cookie data.
          url (str): URL or path where the cookie got set.
        """
        fields = cookie_data.split(".")
        number_of_fields = len(fields)

        if number_of_fields not in (1, 4):
            parser_mediator.ProduceWarning(
                f"unsupported number of fields: {number_of_fields:d} in cookie: "
                f"{self.COOKIE_NAME:s}"
            )
            return

        if number_of_fields == 1:
            domain_hash = None
            number_of_pages_viewed = None

            date_time = self._ParsePosixTimeIn100Nanoseconds(fields[0])

        elif number_of_fields == 4:
            domain_hash = fields[0]

            number_of_pages_viewed = self._ParseIntegerValue(fields[1])

            if fields[2] in ("8", "9"):
                date_time = self._ParsePosixTimeInMilliseconds(fields[3])
            else:
                date_time = self._ParsePosixTime(fields[3])

        else:
            date_time = None
            domain_hash = None
            number_of_pages_viewed = None

        event_data = GoogleAnalyticsUtmbEventData()
        event_data.cookie_name = self.COOKIE_NAME
        event_data.domain_hash = domain_hash
        event_data.last_visited_time = date_time
        event_data.pages_viewed = number_of_pages_viewed
        event_data.url = url

        parser_mediator.ProduceEventData(event_data)


class GoogleAnalyticsUtmtPlugin(interface.BaseCookiePlugin):
    """A browser cookie plugin for __utmt Google Analytics cookies.

    The structure of the cookie data:
    <last time>

    For example:
    13113215173000000
    """

    NAME = "google_analytics_utmt"
    DATA_FORMAT = "Google Analytics __utmt cookie"

    COOKIE_NAME = "__utmt"

    def _ParseCookieData(self, parser_mediator, cookie_data=None, url=None, **kwargs):
        """Extracts events from cookie data.

        Args:
          parser_mediator (ParserMediator): mediates interactions between parsers and
              other components, such as storage and dfVFS.
          cookie_data (bytes): cookie data.
          url (str): URL or path where the cookie got set.
        """
        fields = cookie_data.split(".")
        number_of_fields = len(fields)

        if number_of_fields != 1:
            parser_mediator.ProduceWarning(
                f"unsupported number of fields: {number_of_fields:d} in cookie: "
                f"{self.COOKIE_NAME:s}"
            )
            return

        event_data = GoogleAnalyticsUtmtEventData()
        event_data.cookie_name = self.COOKIE_NAME
        event_data.last_visited_time = self._ParsePosixTimeIn100Nanoseconds(fields[0])
        event_data.url = url

        parser_mediator.ProduceEventData(event_data)


class GoogleAnalyticsUtmzPlugin(interface.BaseCookiePlugin):
    """A browser cookie plugin for __utmz Google Analytics cookies.

    The structure of the cookie data:
    <domain hash>.<last time>.<sessions>.<sources>.<variables>

    For example:
    207318870.1383170190.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|
    utmctr=(not%20provided)

    Or:
    <last time>

    For example:
    13128990382000000
    """

    NAME = "google_analytics_utmz"
    DATA_FORMAT = "Google Analytics __utmz cookie"

    COOKIE_NAME = "__utmz"

    def _ParseCookieData(self, parser_mediator, cookie_data=None, url=None, **kwargs):
        """Extracts events from cookie data.

        Args:
          parser_mediator (ParserMediator): mediates interactions between parsers and
              other components, such as storage and dfVFS.
          cookie_data (str): cookie data.
          url (str): URL or path where the cookie got set.
        """
        fields = cookie_data.split(".")
        number_of_fields = len(fields)

        if number_of_fields > 5:
            variables = ".".join(fields[4:])
            fields = fields[0:4]
            fields.append(variables)
            number_of_fields = len(fields)

        if number_of_fields not in (1, 5):
            parser_mediator.ProduceWarning(
                f"unsupported number of fields: {number_of_fields:d} in cookie: "
                f"{self.COOKIE_NAME:s}"
            )
            return

        if number_of_fields == 1:
            domain_hash = None
            number_of_sessions = None
            number_of_sources = None
            extra_attributes = {}

            date_time = self._ParsePosixTimeIn100Nanoseconds(fields[0])

        elif number_of_fields == 5:
            domain_hash = fields[0]

            date_time = self._ParsePosixTime(fields[1])

            number_of_sessions = self._ParseIntegerValue(fields[2])
            number_of_sources = self._ParseIntegerValue(fields[3])

            extra_variables = fields[4].split("|")

            extra_attributes = {}
            for variable in extra_variables:
                key, _, value = variable.partition("=")
                extra_attributes[key] = urlparse.unquote(value)

        else:
            date_time = None
            domain_hash = None
            number_of_sessions = None
            number_of_sources = None

        event_data = GoogleAnalyticsUtmzEventData()
        event_data.cookie_name = self.COOKIE_NAME
        event_data.domain_hash = domain_hash
        event_data.last_visited_time = date_time
        event_data.sessions = number_of_sessions
        event_data.sources = number_of_sources
        event_data.url = url

        # TODO: explicitly define these as attributes of
        # GoogleAnalyticsUtmzEventData.
        for key, value in extra_attributes.items():
            setattr(event_data, key, value)

        parser_mediator.ProduceEventData(event_data)


manager.CookiePluginsManager.RegisterPlugins(
    [
        GoogleAnalyticsUtmaPlugin,
        GoogleAnalyticsUtmbPlugin,
        GoogleAnalyticsUtmtPlugin,
        GoogleAnalyticsUtmzPlugin,
    ]
)
