#    Copyright (c) 2015, Telefonica I+D.
#
#    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.

from oslo_db import exception as db_exc
from oslo_log import log as logging
from webob import exc

from murano.api.v1 import environments as envs_api
from murano.api.v1 import request_statistics
from murano.common.i18n import _, _LE
from murano.common import policy
from murano.common import utils
from murano.common import wsgi
from murano.db.services import core_services
from murano.db.services import environment_templates as env_temps
from murano.db.services import environments as envs
from murano.db.services import sessions

LOG = logging.getLogger(__name__)

API_NAME = 'Templates'


class Controller(object):
    @request_statistics.stats_count(API_NAME, 'Index')
    def index(self, request):
        """It lists the env templates associated to an tenant-id.
        :param request: The operation request.
        :return: the env template description list.
        """
        LOG.debug('EnvTemplates:List')
        policy.check('list_env_templates', request.context)

        filters = {'tenant_id': request.context.tenant}
        list_templates = env_temps.EnvTemplateServices.\
            get_env_templates_by(filters)
        list_templates = [temp.to_dict() for temp in list_templates]

        return {"templates": list_templates}

    @request_statistics.stats_count(API_NAME, 'Create')
    def create(self, request, body):
        """It creates the env template from the payload obtaining.
        This payload can contain just the template name, or include
        also service information.
        :param request: the operation request.
        :param body: the env template description
        :return: the description of the created template.
        """
        LOG.debug('EnvTemplates:Create <Body {body}>'.format(body=body))
        policy.check('create_env_template', request.context)
        try:
            LOG.debug('ENV TEMP NAME: {templ_name}>'.format(
                templ_name=body['name']))
            if not envs_api.VALID_NAME_REGEX.match(str(body['name'])):
                msg = _('Environment Template must contain only alphanumeric '
                        'or "_-." characters, must start with alpha')
                LOG.error(msg)
                raise exc.HTTPBadRequest(msg)
        except Exception:
                msg = _('Env template body is incorrect')
                LOG.exception(msg)
                raise exc.HTTPClientError(msg)
        if len(body['name']) > 255:
            msg = _('Environment Template name should be 255 characters '
                    'maximum')
            LOG.exception(msg)
            raise exc.HTTPBadRequest(explanation=msg)
        try:
            template = env_temps.EnvTemplateServices.create(
                body.copy(), request.context.tenant)
            return template.to_dict()
        except db_exc.DBDuplicateEntry:
            msg = _('Env Template with specified name already exists')
            LOG.exception(msg)
            raise exc.HTTPConflict(msg)

    @request_statistics.stats_count(API_NAME, 'Show')
    def show(self, request, env_template_id):
        """It shows the description about a template.
        :param request: the operation request.
        :param env_template_id: the env template ID.
        :return: the description of the env template.
        """
        LOG.debug('Templates:Show <Id: {templ_id}>'.format(
            templ_id=env_template_id))
        target = {"env_template_id": env_template_id}
        policy.check('show_env_template', request.context, target)

        self._validate_request(request, env_template_id)

        template = env_temps.EnvTemplateServices.\
            get_env_template(env_template_id)
        temp = template.to_dict()

        get_data = core_services.CoreServices.get_template_data
        temp['services'] = get_data(env_template_id, '/services')
        return temp

    @request_statistics.stats_count(API_NAME, 'Update')
    def update(self, request, env_template_id, body):
        """It updates the description template.
        :param request: the operation request.
        :param env_template_id: the env template ID.
        :param body: the description to be updated
        :return: the updated template description.
        """
        LOG.debug('Templates:Update <Id: {templ_id}, '
                  'Body: {body}>'.format(templ_id=env_template_id, body=body))
        target = {"env_template_id": env_template_id}
        policy.check('update_env_template', request.context, target)

        self._validate_request(request, env_template_id)
        try:
            LOG.debug('ENV TEMP NAME: {temp_name}>'.format(
                temp_name=body['name']))
            if not envs_api.VALID_NAME_REGEX.match(str(body['name'])):
                msg = _('Env Template must contain only alphanumeric '
                        'or "_-." characters, must start with alpha')
                LOG.exception(msg)
                raise exc.HTTPBadRequest(msg)
        except Exception:
                msg = _('EnvTemplate body is incorrect')
                LOG.exception(msg)
                raise exc.HTTPBadRequest(msg)

        template = env_temps.EnvTemplateServices.update(env_template_id, body)
        return template.to_dict()

    @request_statistics.stats_count(API_NAME, 'Delete')
    def delete(self, request, env_template_id):
        """It deletes the env template.
        :param request: the operation request.
        :param env_template_id: the template ID.
        """
        LOG.debug('EnvTemplates:Delete <Id: {templ_id}>'.format(
            templ_id=env_template_id))
        target = {"env_template_id": env_template_id}
        policy.check('delete_env_template', request.context, target)
        self._validate_request(request, env_template_id)
        env_temps.EnvTemplateServices.delete(env_template_id)
        env_temps.EnvTemplateServices.remove(env_template_id)
        return

    def has_services(self, template):
        """"It checks if the template has services
        :param template: the template to check.
        :return: True or False
        """
        if not template.description:
            return False

        if (template.description.get('services')):
            return True
        return False

    @request_statistics.stats_count(API_NAME, 'Create_environment')
    def create_environment(self, request, env_template_id, body):
        """Creates environment and session from template.
        :param request: operation request
        :param env_template_id: environment template ID
        :param body: the environment name
        :return: session_id and environment_id
        """
        LOG.debug('Templates:Create environment <Id: {templ_id}>'.
                  format(templ_id=env_template_id))
        target = {"env_template_id": env_template_id}
        policy.check('create_environment', request.context, target)

        self._validate_request(request, env_template_id)
        template = env_temps.EnvTemplateServices.\
            get_env_template(env_template_id)

        if ('name' not in body or
                not envs_api.VALID_NAME_REGEX.match(str(body['name']))):
            msg = _('Environment must contain only alphanumeric '
                    'or "_-." characters, must start with alpha')
            LOG.error(msg)
            raise exc.HTTPBadRequest(explanation=msg)
        LOG.debug('ENVIRONMENT NAME: {env_name}>'.format(
            env_name=body['name']))

        try:
            environment = envs.EnvironmentServices.create(
                body.copy(), request.context)
        except db_exc.DBDuplicateEntry:
            msg = _('Environment with specified name already exists')
            LOG.exception(msg)
            raise exc.HTTPConflict(explanation=msg)

        user_id = request.context.user
        session = sessions.SessionServices.create(environment.id, user_id)

        if self.has_services(template):
            services_node = utils.TraverseHelper.get("services",
                                                     template.description)
            utils.TraverseHelper.update("/Objects/services",
                                        services_node,
                                        environment.description)

        envs.EnvironmentServices.save_environment_description(
            session.id,
            environment.description,
            inner=False
        )
        return {"session_id": session.id, "environment_id": environment.id}

    def _validate_request(self, request, env_template_id):
        env_template_exists = env_temps.EnvTemplateServices.env_template_exist
        if not env_template_exists(env_template_id):
            msg = _('EnvTemplate <TempId {temp_id}> is not found').format(
                temp_id=env_template_id)
            LOG.exception(msg)
            raise exc.HTTPNotFound(explanation=msg)
        get_env_template = env_temps.EnvTemplateServices.get_env_template
        env_template = get_env_template(env_template_id)
        if env_template.tenant_id != request.context.tenant:
            LOG.exception(_LE('User is not authorized to access this tenant '
                              'resources.'))
            raise exc.HTTPUnauthorized


def create_resource():
    return wsgi.Resource(Controller())
