# Copyright 2015 kornicameister@gmail.com
# Copyright 2016 FUJITSU LIMITED
#
# 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 datetime

from monasca_common.rest import utils as rest_utils
from oslo_config import cfg
from oslo_log import log

from monasca_log_api.app.base import exceptions
from monasca_log_api.app.base import model
from monasca_log_api.app.base import validation
from monasca_log_api import conf

LOG = log.getLogger(__name__)
CONF = conf.CONF

EPOCH_START = datetime.datetime(1970, 1, 1)


class LogCreator(object):
    """Transforms logs,

    Takes care of transforming information received via
    HTTP requests into log and log envelopes objects.

    For more details see following:

    * :py:func:`LogCreator.new_log`
    * :py:func:`LogCreator.new_log_envelope`

    """

    def __init__(self):
        self._log = log.getLogger('service.LogCreator')
        self._log.info('Initializing LogCreator')

    @staticmethod
    def _create_meta_info(tenant_id):
        """Creates meta block for log envelope.

        Additionally method accesses oslo configuration,
        looking for *service.region* configuration property.

        For more details see :py:data:`service_opts`

        :param tenant_id: ID of the tenant
        :type tenant_id: str
        :return: meta block
        :rtype: dict

        """
        return {
            'tenantId': tenant_id,
            'region': cfg.CONF.service.region
        }

    def new_log(self,
                application_type,
                dimensions,
                payload,
                content_type='application/json',
                validate=True):
        """Creates new log object.

        :param str application_type: origin of the log
        :param dict dimensions: dictionary of dimensions (any data sent to api)
        :param stream payload: stream to read log entry from
        :param str content_type: actual content type used to send data to
                                 server
        :param bool validate: by default True, marks if log should be validated
        :return: log object
        :rtype: dict

        :keyword: log_object
        """

        payload = rest_utils.read_body(payload, content_type)
        if not payload:
            return None

        # normalize_yet_again
        application_type = parse_application_type(application_type)
        dimensions = parse_dimensions(dimensions)

        if validate:
            self._log.debug('Validation enabled, proceeding with validation')
            validation.validate_application_type(application_type)
            validation.validate_dimensions(dimensions)

        self._log.debug(
            'application_type=%s,dimensions=%s' % (
                application_type, dimensions)
        )

        log_object = {}
        if content_type == 'application/json':
            log_object.update(payload)
        else:
            log_object.update({'message': payload})

        validation.validate_log_message(log_object)

        dimensions['component'] = application_type
        log_object.update({'dimensions': dimensions})

        return log_object

    def new_log_envelope(self, log_object, tenant_id):
        return model.Envelope(
            log=log_object,
            meta=self._create_meta_info(tenant_id)
        )


def parse_application_type(app_type):
    if app_type:
        app_type = app_type.strip()
    return app_type if app_type else None


def parse_dimensions(dimensions):
    if not dimensions:
        raise exceptions.HTTPUnprocessableEntity('Dimension are required')

    new_dimensions = {}
    dimensions = map(str.strip, dimensions.split(','))

    for dim in dimensions:
        if not dim:
            raise exceptions.HTTPUnprocessableEntity(
                'Dimension cannot be empty')
        elif ':' not in dim:
            raise exceptions.HTTPUnprocessableEntity(
                '%s is not a valid dimension' % dim)

        dim = dim.split(':')
        name = str(dim[0].strip()) if dim[0] else None
        value = str(dim[1].strip()) if dim[1] else None
        if name and value:
            new_dimensions.update({name: value})

    return new_dimensions
