# 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 flask
from flask import current_app as app
import flask_restful
import six
from sqlalchemy import or_

from tuning_box import db
from tuning_box import library
from tuning_box.library import hierarchy_levels
from tuning_box.library import resource_keys_operation


class ResourceValues(flask_restful.Resource):

    @db.with_transaction
    def put(self, environment_id, levels, resource_id_or_name):
        environment = db.Environment.query.get_or_404(environment_id)
        res_def = library.get_resource_definition(
            resource_id_or_name, environment_id)

        level_value = hierarchy_levels.get_environment_level_value(
            environment, levels)
        esv = db.get_or_create(
            db.ResourceValues,
            environment=environment,
            resource_definition=res_def,
            level_value=level_value,
        )
        esv.values = flask.request.json
        return None, 204

    def _calculate_effective_values(self, result, level_value,
                                    resource_values_idx, show_lookup,
                                    lookup_path):
        level_value_id = getattr(level_value, 'id', None)
        if level_value_id in resource_values_idx:
            resource_value = resource_values_idx[level_value_id]
            if show_lookup:
                values = ((k, (v, lookup_path)) for k, v in
                          six.iteritems(resource_value.values))
                overrides = ((k, (v, lookup_path)) for k, v in
                             six.iteritems(resource_value.overrides))
            else:
                values = resource_value.values
                overrides = resource_value.overrides
            result.update(values)
            result.update(overrides)

    @db.with_transaction
    def get(self, environment_id, resource_id_or_name, levels):
        app.logger.debug("Getting resource value. Env: %s, "
                         "resource: %s, levels: %s", environment_id,
                         resource_id_or_name, levels)
        environment = db.Environment.query.get_or_404(environment_id)
        res_def = library.get_resource_definition(
            resource_id_or_name, environment_id)

        level_values = list(hierarchy_levels.iter_environment_level_values(
            environment, levels))

        level_values_ids = [l.id for l in level_values]
        app.logger.debug("Got level values ids: %s", level_values_ids)

        if 'effective' in flask.request.args:
            app.logger.debug("Getting effective resource value. Env: %s, "
                             "resource: %s, levels: %s", environment_id,
                             resource_id_or_name, levels)
            show_lookup = 'show_lookup' in flask.request.args
            resource_values = db.ResourceValues.query.filter(
                or_(
                    db.ResourceValues.level_value_id.in_(level_values_ids),
                    db.ResourceValues.level_value_id.is_(None)
                ),
                db.ResourceValues.resource_definition == res_def,
                db.ResourceValues.environment == environment
            ).all()
            app.logger.debug("Processing values for resource: %s, env: %s. "
                             "Loaded resource values: %s",
                             res_def.id, environment.id, len(resource_values))
            # Creating index of resource_values by level_value_id
            resource_values_idx = {r.level_value_id: r
                                   for r in resource_values}
            app.logger.debug("Resource values index size: %s",
                             len(resource_values_idx))

            result = {}
            lookup_path = '/'
            self._calculate_effective_values(
                result, None, resource_values_idx, show_lookup,
                lookup_path)

            for level_value in level_values:
                name = level_value.level.name
                value = level_value.value
                lookup_path += name + '/' + value + '/'

                self._calculate_effective_values(
                    result, level_value, resource_values_idx, show_lookup,
                    lookup_path)

            app.logger.debug("Effective values got for resource: "
                             "%s, env: %s", res_def.id, environment.id)
            return result
        else:
            if not level_values:
                level_value = None
            else:
                level_value = level_values[-1]
            resource_values = db.ResourceValues.query.filter_by(
                resource_definition=res_def,
                environment=environment,
                level_value=level_value,
            ).one_or_none()
            app.logger.debug("Values got for resource: "
                             "%s, env: %s", res_def.id, environment.id)
            if not resource_values:
                return {}
            return resource_values.values


class ResourceValuesKeys(flask_restful.Resource,
                         resource_keys_operation.ResourceKeysMixin):

    def put(self, environment_id, levels, resource_id_or_name, operation):
        return self.patch(environment_id, levels,
                          resource_id_or_name, operation)

    def patch(self, environment_id, levels, resource_id_or_name, operation):
        self._do_update(environment_id, levels, resource_id_or_name,
                        operation, 'values')
        return None, 204
