# -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# ex: set expandtab softtabstop=4 shiftwidth=4:
#
# Copyright (C) 2010-2015,2018  Contributor
#
# 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.
"""Contains the logic for `aq search metacluster`."""

from sqlalchemy.orm import aliased
from sqlalchemy.sql import or_

from aquilon.exceptions_ import NotFoundException
from aquilon.worker.broker import BrokerCommand
from aquilon.worker.formats.list import StringAttributeList
from aquilon.aqdb.model import (Cluster, MetaCluster, Archetype, Personality,
                                PersonalityStage, ClusterLifecycle, Service,
                                ServiceInstance, Share, ClusterResource,
                                BundleResource, ResourceGroup, User)
from aquilon.worker.dbwrappers.branch import get_branch_and_author
from aquilon.worker.dbwrappers.location import get_location


class CommandSearchMetaCluster(BrokerCommand):

    required_parameters = []

    def render(self, session, archetype, personality, personality_stage,
               domain, sandbox, branch, sandbox_author, buildstatus,
               allowed_archetype, allowed_personality, max_members,
               member_archetype, member_cluster, member_personality,
               member_exact_location, metacluster_exact_location,
               metacluster, service, instance, share,
               fullinfo, style, **arguments):

        # Don't load full objects if we only want to show their name
        if fullinfo or style != 'raw':
            q = session.query(MetaCluster)
        else:
            q = session.query(MetaCluster.name)

        # The ORM automatically de-duplicates the result if we query full
        # objects, but not when we query just the names. Tell the DB to do so.
        q = q.distinct()

        dbbranch, dbauthor = get_branch_and_author(session, domain=domain,
                                                   sandbox=sandbox,
                                                   branch=branch)
        if sandbox_author:
            dbauthor = User.get_unique(session, sandbox_author, compel=True)

        if dbbranch:
            q = q.filter_by(branch=dbbranch)
        if dbauthor:
            q = q.filter_by(sandbox_author=dbauthor)

        if archetype:
            # Added to the searches as appropriate below.
            dbarchetype = Archetype.get_unique(session, archetype, compel=True)
        else:
            dbarchetype = None

        if personality or archetype:
            q = q.join(PersonalityStage, aliased=True)
            if personality_stage:
                Personality.force_valid_stage(personality_stage)
                q = q.filter_by(name=personality_stage)
            if personality:
                subq = Personality.get_matching_query(session, name=personality,
                                                      archetype=dbarchetype,
                                                      compel=True)
                q = q.filter(PersonalityStage.personality_id.in_(subq))
            elif archetype:
                q = q.join(Personality, aliased=True, from_joinpoint=True)
                q = q.filter_by(archetype=dbarchetype)

            q = q.reset_joinpoint()

        if buildstatus:
            dbbuildstatus = ClusterLifecycle.get_instance(session, buildstatus)
            q = q.filter_by(status=dbbuildstatus)

        dblocation = get_location(
            session, locfunc=lambda x: 'metacluster_{}'.format(x), **arguments)
        if dblocation:
            if metacluster_exact_location:
                q = q.filter_by(location_constraint=dblocation)
            else:
                childids = dblocation.offspring_ids()
                q = q.filter(MetaCluster.location_constraint_id.in_(childids))

        dblocation = get_location(
            session, locfunc=lambda x: 'member_{}'.format(x), **arguments)
        if dblocation:
            CAlias = aliased(Cluster)
            q = q.join(CAlias, MetaCluster.members)
            if member_exact_location:
                q = q.filter_by(location_constraint=dblocation)
            else:
                childids = dblocation.offspring_ids()
                q = q.filter(CAlias.location_constraint_id.in_(childids))
            q = q.reset_joinpoint()

        if metacluster:
            q = q.filter_by(name=metacluster)

        if service:
            dbservice = Service.get_unique(session, name=service, compel=True)
            if instance:
                dbsi = ServiceInstance.get_unique(session, name=instance,
                                                  service=dbservice,
                                                  compel=True)
                q = q.filter(MetaCluster.services_used.contains(dbsi))
            else:
                q = q.join('services_used')
                q = q.filter_by(service=dbservice)
                q = q.reset_joinpoint()
        elif instance:
            q = q.join('services_used')
            q = q.filter_by(name=instance)
            q = q.reset_joinpoint()

        if share:
            # Perform sanity check on the share name
            q2 = session.query(Share.id)
            q2 = q2.filter_by(name=share)
            if not q2.first():
                raise NotFoundException("Share %s not found." % share)

            CR = aliased(ClusterResource)
            S1 = aliased(Share)
            S2 = aliased(Share)
            RG = aliased(ResourceGroup)
            BR = aliased(BundleResource)