# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2010-2011 OpenStack Foundation.
# Copyright 2012 Justin Santa Barbara
# All Rights Reserved.
#
#    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 oslo_db_exception
from oslo_db.sqlalchemy import utils
from oslo_log import log
import sqlalchemy
from sqlalchemy import exc as sqlalchemy_exc
from sqlalchemy import select


from designate import exceptions
from designate.i18n import _
from designate.storage import sql

LOG = log.getLogger(__name__)

RRSET_FILTERING_INDEX = {
    'created_at': 'recordset_created_at',
    'updated_at': 'rrset_updated_at',
    'zone_id': 'rrset_zoneid',
    'name': 'recordset_type_name',
    'type': 'rrset_type',
    'ttl': 'rrset_ttl',
    'tenant_id': 'rrset_tenant_id',
}


# copy from olso/db/sqlalchemy/utils.py
def paginate_query(query, table, limit, sort_keys, marker=None,
                   sort_dir=None, sort_dirs=None):

    # Add sorting
    query, sort_dirs = sort_query(query, table, sort_keys, sort_dir=sort_dir)

    # Add pagination
    if marker is not None:
        marker_values = []
        for sort_key in sort_keys:
            v = getattr(marker, sort_key)
            marker_values.append(v)

        # Build up an array of sort criteria as in the docstring
        criteria_list = []
        for i in range(len(sort_keys)):
            crit_attrs = []
            for j in range(i):
                table_attr = getattr(table.c, sort_keys[j])
                crit_attrs.append(table_attr == marker_values[j])

            table_attr = getattr(table.c, sort_keys[i])
            if sort_dirs[i] == 'desc':
                crit_attrs.append(table_attr < marker_values[i])
            else:
                crit_attrs.append(table_attr > marker_values[i])

            criteria = sqlalchemy.sql.and_(*crit_attrs)
            criteria_list.append(criteria)

        f = sqlalchemy.sql.or_(*criteria_list)
        query = query.where(f)

    if limit is not None:
        query = query.limit(limit)

    return query


def sort_query(query, table, sort_keys, sort_dir=None, sort_dirs=None):

    if 'id' not in sort_keys:
        # TODO(justinsb): If this ever gives a false-positive, check
        # the actual primary key, rather than assuming its id
        LOG.warning('Id not in sort_keys; is sort_keys unique?')

    assert (not (sort_dir and sort_dirs))

    # Default the sort direction to ascending
    if sort_dirs is None and sort_dir is None:
        sort_dir = 'asc'

    # Ensure a per-column sort direction
    if sort_dirs is None:
        sort_dirs = [sort_dir for _sort_key in sort_keys]

    assert (len(sort_dirs) == len(sort_keys))

    for current_sort_key, current_sort_dir in zip(sort_keys, sort_dirs):
        try:
            sort_dir_func = {
                'asc': sqlalchemy.asc,
                'desc': sqlalchemy.desc,
            }[current_sort_dir]
        except KeyError:
            raise ValueError(_("Unknown sort direction, "
                               "must be 'desc' or 'asc'"))
        try:
            sort_key_attr = getattr(table.c, current_sort_key)
        except AttributeError:
            raise utils.InvalidSortKey()
        query = query.order_by(sort_dir_func(sort_key_attr))

    return query, sort_dirs


def check_marker(table, marker):

    marker_query = select(table).where(table.c.id == marker)

    try:
        with sql.get_read_session() as session:
            marker_resultproxy = session.execute(marker_query)
            marker = marker_resultproxy.fetchone()
        if marker is None:
            raise exceptions.MarkerNotFound(
                'Marker %s could not be found' % marker)
    except oslo_db_exception.DBError as e:
        # Malformed UUIDs return StatementError wrapped in a
        # DBError
        if isinstance(e.inner_exception,
                      sqlalchemy_exc.StatementError):
            raise exceptions.InvalidMarker()
        else:
            raise

    return marker


def get_rrset_index(sort_key):
    rrset_index_hint = None
    index = RRSET_FILTERING_INDEX.get(sort_key)

    if index:
        rrset_index_hint = 'USE INDEX (%s)' % index

    return rrset_index_hint
