# Copyright 2017 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import functools
import operator
import unittest

from google.cloud import error_reporting
from google.cloud.gapic.errorreporting.v1beta1 import (
    error_stats_service_client)
from google.cloud.proto.devtools.clouderrorreporting.v1beta1 import (
    error_stats_service_pb2)
from google.protobuf.duration_pb2 import Duration

from test_utils.retry import RetryResult
from test_utils.system import unique_resource_id


ERROR_MSG = 'Stackdriver Error Reporting System Test'


def setUpModule():
    Config.CLIENT = error_reporting.Client()


class Config(object):
    """Run-time configuration to be modified at set-up.

    This is a mutable stand-in to allow test set-up to modify
    global state.
    """
    CLIENT = None


def _list_groups(client):
    """List Error Groups from the last 60 seconds.

    This class provides a wrapper around making calls to the GAX
    API. It's used by the system tests to find the appropriate error group
    to verify the error was successfully reported.

    :type client: :class:`~google.cloud.error_reporting.client.Client`
    :param client: The client containing a project and credentials.

    :rtype: :class:`~google.gax.ResourceIterator`
    :returns: Iterable of :class:`~.error_stats_service_pb2.ErrorGroupStats`.
    """
    gax_api = error_stats_service_client.ErrorStatsServiceClient(
        credentials=client._credentials)
    project_name = gax_api.project_path(client.project)

    time_range = error_stats_service_pb2.QueryTimeRange()
    time_range.period = error_stats_service_pb2.QueryTimeRange.PERIOD_1_HOUR

    duration = Duration(seconds=60 * 60)

    return gax_api.list_group_stats(
        project_name, time_range, timed_count_duration=duration)


def _simulate_exception(class_name, client):
    """Simulates an exception to verify it was reported.

    :type class_name: str
    :param class_name: The name of a custom error class to
                       create (and raise).

    :type client: :class:`~google.cloud.error_reporting.client.Client`
    :param client: The client that will report the exception.
    """
    custom_exc = type(class_name, (RuntimeError,), {})
    try:
        raise custom_exc(ERROR_MSG)
    except RuntimeError:
        client.report_exception()


def _get_error_count(class_name, client):
    """Counts the number of errors in the group of the test exception.

    :type class_name: str
    :param class_name: The name of a custom error class used.

    :type client: :class:`~google.cloud.error_reporting.client.Client`
    :param client: The client containing a project and credentials.

    :rtype: int
    :returns: Group count for errors that match ``class_name``. If no
              match is found, returns :data:`None`.
    """
    groups = _list_groups(client)
    for group in groups:
        if class_name in group.representative.message:
            return group.count


class TestErrorReporting(unittest.TestCase):

    def test_report_exception(self):
        # Get a class name unique to this test case.
        class_name = 'RuntimeError' + unique_resource_id('_')

        # Simulate an error: group won't exist until we report
        # first exception.
        _simulate_exception(class_name, Config.CLIENT)

        is_one = functools.partial(operator.eq, 1)
        is_one.__name__ = 'is_one'  # partial() has no name.
        retry = RetryResult(is_one, max_tries=6)
        wrapped_get_count = retry(_get_error_count)

        error_count = wrapped_get_count(class_name, Config.CLIENT)
        self.assertEqual(error_count, 1)
