# Copyright (c) 2015 Huawei Tech. Co., Ltd.
# 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.

import pecan
from pecan import expose
from pecan import Response
from pecan import rest

import oslo_db.exception as db_exc
from oslo_log import log as logging
from oslo_utils import uuidutils

from tricircle.common import az_ag
import tricircle.common.context as t_context
import tricircle.common.exceptions as t_exc
from tricircle.common.i18n import _
from tricircle.common.i18n import _LE
from tricircle.common import utils

from tricircle.db import api as db_api
from tricircle.db import core
from tricircle.db import models

LOG = logging.getLogger(__name__)


class PodsController(rest.RestController):

    def __init__(self):
        pass

    @expose(generic=True, template='json')
    def post(self, **kw):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to create pods'))
            return

        if 'pod' not in kw:
            pecan.abort(400, _('Request body pod not found'))
            return

        pod = kw['pod']

        # if az_name is null, and there is already one in db
        pod_name = pod.get('pod_name', '').strip()
        pod_az_name = pod.get('pod_az_name', '').strip()
        dc_name = pod.get('dc_name', '').strip()
        az_name = pod.get('az_name', '').strip()
        _uuid = uuidutils.generate_uuid()

        if az_name == '' and pod_name == '':
            return Response(_('Valid pod_name is required for top region'),
                            422)

        if az_name != '' and pod_name == '':
            return Response(_('Valid pod_name is required for pod'), 422)

        if pod.get('az_name') is None:
            if self._get_top_region(context) != '':
                return Response(_('Top region already exists'), 409)

        # if az_name is not null, then the pod region name should not
        # be same as that the top region
        if az_name != '':
            if self._get_top_region(context) == pod_name and pod_name != '':
                return Response(
                    _('Pod region name duplicated with the top region name'),
                    409)

        # to create the top region, make the pod_az_name to null value
        if az_name == '':
            pod_az_name = ''

        try:
            with context.session.begin():
                # if not top region,
                # then add corresponding ag and az for the pod
                if az_name != '':
                    ag_name = utils.get_ag_name(pod_name)
                    aggregate = az_ag.create_ag_az(context,
                                                   ag_name=ag_name,
                                                   az_name=az_name)
                    if aggregate is None:
                        return Response(_('Ag creation failure'), 400)

                new_pod = core.create_resource(
                    context, models.Pod,
                    {'pod_id': _uuid,
                     'pod_name': pod_name,
                     'pod_az_name': pod_az_name,
                     'dc_name': dc_name,
                     'az_name': az_name})
        except db_exc.DBDuplicateEntry as e1:
            LOG.error(_LE('Record already exists: %(exception)s'),
                      {'exception': e1})
            return Response(_('Record already exists'), 409)
        except Exception as e2:
            LOG.error(_LE('Fail to create pod: %(exception)s'),
                      {'exception': e2})
            return Response(_('Fail to create pod'), 500)

        return {'pod': new_pod}

    @expose(generic=True, template='json')
    def get_one(self, _id):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to show pods'))
            return

        try:
            return {'pod': db_api.get_pod(context, _id)}
        except t_exc.ResourceNotFound:
            pecan.abort(404, _('Pod not found'))
            return

    @expose(generic=True, template='json')
    def get_all(self):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to list pods'))
            return

        try:
            return {'pods': db_api.list_pods(context)}
        except Exception as e:
            LOG.error(_LE('Fail to list pod: %(exception)s'),
                      {'exception': e})
            pecan.abort(500, _('Fail to list pod'))
            return

    @expose(generic=True, template='json')
    def delete(self, _id):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to delete pods'))
            return

        try:
            with context.session.begin():
                pod = core.get_resource(context, models.Pod, _id)
                if pod is not None:
                    ag_name = utils.get_ag_name(pod['pod_name'])
                    ag = az_ag.get_ag_by_name(context, ag_name)
                    if ag is not None:
                        az_ag.delete_ag(context, ag['id'])
                core.delete_resource(context, models.Pod, _id)
                pecan.response.status = 200
        except t_exc.ResourceNotFound:
            return Response(_('Pod not found'), 404)
        except Exception as e:
            LOG.error(_LE('Fail to delete pod: %(exception)s'),
                      {'exception': e})
            return Response(_('Fail to delete pod'), 500)

    def _get_top_region(self, ctx):
        top_region_name = ''
        try:
            with ctx.session.begin():
                pods = core.query_resource(ctx,
                                           models.Pod, [], [])
                for pod in pods:
                    if pod['az_name'] == '' and pod['pod_name'] != '':
                        return pod['pod_name']
        except Exception:
            return top_region_name

        return top_region_name


class BindingsController(rest.RestController):

    def __init__(self):
        pass

    @expose(generic=True, template='json')
    def post(self, **kw):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to create bindings'))
            return

        if 'pod_binding' not in kw:
            pecan.abort(400, _('Request body not found'))
            return

        pod_b = kw['pod_binding']
        tenant_id = pod_b.get('tenant_id', '').strip()
        pod_id = pod_b.get('pod_id', '').strip()
        _uuid = uuidutils.generate_uuid()

        if tenant_id == '' or pod_id == '':
            return Response(
                _('Tenant_id and pod_id can not be empty'),
                422)

        # the az_pod_map_id should be exist for in the pod map table
        try:
            with context.session.begin():
                pod = core.get_resource(context, models.Pod,
                                        pod_id)
                if pod.get('az_name') == '':
                    return Response(_('Top region can not be bound'), 422)
        except t_exc.ResourceNotFound:
            return Response(_('pod_id not found in pod'), 422)
        except Exception as e:
            LOG.error(_LE('Fail to create pod binding: %(exception)s'),
                      {'exception': e})
            pecan.abort(500, _('Fail to create pod binding'))
            return

        try:
            with context.session.begin():
                pod_binding = core.create_resource(context, models.PodBinding,
                                                   {'id': _uuid,
                                                    'tenant_id': tenant_id,
                                                    'pod_id': pod_id})
        except db_exc.DBDuplicateEntry:
            return Response(_('Pod binding already exists'), 409)
        except db_exc.DBConstraintError:
            return Response(_('pod_id not exists in pod'), 422)
        except db_exc.DBReferenceError:
            return Response(_('DB reference not exists in pod'), 422)
        except Exception as e:
            LOG.error(_LE('Fail to create pod binding: %(exception)s'),
                      {'exception': e})
            pecan.abort(500, _('Fail to create pod binding'))
            return

        return {'pod_binding': pod_binding}

    @expose(generic=True, template='json')
    def get_one(self, _id):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to show bindings'))
            return

        try:
            with context.session.begin():
                pod_binding = core.get_resource(context,
                                                models.PodBinding,
                                                _id)
                return {'pod_binding': pod_binding}
        except t_exc.ResourceNotFound:
            pecan.abort(404, _('Tenant pod binding not found'))
            return

    @expose(generic=True, template='json')
    def get_all(self):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to list bindings'))
            return

        try:
            with context.session.begin():
                pod_bindings = core.query_resource(context,
                                                   models.PodBinding,
                                                   [], [])
        except Exception:
            pecan.abort(500, _('Fail to list tenant pod bindings'))
            return

        return {'pod_bindings': pod_bindings}

    @expose(generic=True, template='json')
    def delete(self, _id):
        context = t_context.extract_context_from_environ()

        if not t_context.is_admin_context(context):
            pecan.abort(400, _('Admin role required to delete bindings'))
            return

        try:
            with context.session.begin():
                core.delete_resource(context, models.PodBinding, _id)
                pecan.response.status = 200
        except t_exc.ResourceNotFound:
            pecan.abort(404, _('Pod binding not found'))
            return
