#!/usr/bin/env python
# Copyright 2015 Symantec Corporation
# Copyright 2013 Mirantis Inc.
# 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 os
import sys
import time

from oslo.config import cfg
from oslo import messaging

import eventlet
eventlet.patcher.monkey_patch(all=True)

# If ../PRODUCT_NAME/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
                                                os.pardir,
                                                os.pardir))
if os.path.exists(os.path.join(possible_topdir, "magnetodb", '__init__.py')):
    sys.path.insert(0, possible_topdir)


from magnetodb import context as ctxt
from magnetodb import notifier
from magnetodb.openstack.common import log as logging
from magnetodb.common import exception
from magnetodb import storage
from magnetodb.storage import models


CONF = cfg.CONF
LOG = logging.getLogger(__name__)


class SchemaEndpoint(object):
    def __init__(self):
        self._notifier = notifier.get_notifier()

        LOG.debug('Creating endpoint...')

        context = storage.load_context(CONF)
        self._storage_driver = context["storage_driver"]
        self._table_info_repo = context["table_info_repo"]

        LOG.debug('Endpoint created')

    def create(self, ctx, table_name):
        LOG.debug("Start creating table '%s'", table_name)

        context = ctxt.RequestContext.from_dict(ctx)
        start_time = time.time()

        try:
            table_info = self._table_info_repo.get(context, table_name)
        except exception.TableNotExistsException as e:
            self._notifier.error(
                context,
                notifier.EVENT_TYPE_TABLE_CREATE_ERROR,
                dict(
                    table_name=table_name,
                    table_uuid="",
                    message=e.message,
                    value=start_time
                )
            )
            LOG.error("Create table failed."
                      " Table info for table '%s' does not exist in repo",
                      table_name)
            return

        if table_info.status != models.TableMeta.TABLE_STATUS_CREATING:
            e = exception.ResourceInUseException()
            self._notifier.error(
                context,
                notifier.EVENT_TYPE_TABLE_CREATE_ERROR,
                dict(
                    table_name=table_name,
                    table_uuid=str(table_info.id),
                    message=e.message,
                    value=start_time
                )
            )

            LOG.error("Create table failed. Table '%s' with uuid '%s' is in "
                      "%s state but %s is expected",
                      table_name,
                      str(table_info.id),
                      table_info.status,
                      models.TableMeta.TABLE_STATUS_CREATING)
            return

        try:
            internal_name = self._storage_driver.create_table(
                context, table_info)

            table_info.status = models.TableMeta.TABLE_STATUS_ACTIVE

            table_info.internal_name = internal_name
            self._table_info_repo.update(
                context, table_info, ["status", "internal_name"])
            self._notifier.audit(
                context,
                notifier.EVENT_TYPE_TABLE_CREATE,
                dict(
                    table_name=table_name,
                    table_uuid=str(table_info.id),
                    index_count=len(table_info.schema['index_def_map'].keys()),
                    value=start_time
                )
            )

            LOG.debug("Table '%s' with uuid %s created", table_name,
                      str(table_info.id))
        except Exception as e:
            self._notifier.error(
                context,
                notifier.EVENT_TYPE_TABLE_CREATE_ERROR,
                dict(
                    table_name=table_name,
                    table_uuid=str(table_info.id),
                    message=e.message,
                    value=start_time
                )
            )
            table_info.status = models.TableMeta.TABLE_STATUS_CREATE_FAILED
            self._table_info_repo.update(context, table_info, ["status"])

            LOG.error("Create table '%s' with uuid %s failed. %s",
                      table_name, str(table_info.id), str(e))

    def delete(self, ctx, table_name):
        LOG.debug("Start deleting table '%s'", table_name)
        start_time = time.time()

        context = ctxt.RequestContext.from_dict(ctx)

        try:
            table_info = self._table_info_repo.get(context, table_name)
        except exception.TableNotExistsException as e:
            self._notifier.error(
                context,
                notifier.EVENT_TYPE_TABLE_DELETE_ERROR,
                dict(
                    table_name=table_name,
                    message=e.message,
                    value=start_time
                )
            )

            LOG.error("Delete table failed."
                      " Table info for table '%s' does not exist in repo",
                      table_name)

            return

        if table_info.status != models.TableMeta.TABLE_STATUS_DELETING:
            e = exception.ResourceInUseException()
            self._notifier.error(
                context,
                notifier.EVENT_TYPE_TABLE_DELETE_ERROR,
                dict(
                    table_name=table_name,
                    table_uuid=str(table_info.id),
                    message=e.message,
                    value=start_time
                )
            )

            LOG.error("Delete table failed. Table '%s' with uuid %s is in %s "
                      "state but %s is expected",
                      table_name,
                      str(table_info.id),
                      table_info.status,
                      models.TableMeta.TABLE_STATUS_DELETING)

            return

        try:
            self._storage_driver.delete_table(context, table_info)
            self._table_info_repo.delete(context, table_name)
            self._notifier.audit(
                context,
                notifier.EVENT_TYPE_TABLE_DELETE,
                dict(
                    table_name=table_name,
                    table_uuid=str(table_info.id),
                    value=start_time
                )
            )
            LOG.debug("Table '%s' with uuid %s deleted", table_name,
                      str(table_info.id))
        except Exception as e:
            self._notifier.error(
                context,
                notifier.EVENT_TYPE_TABLE_DELETE_ERROR,
                dict(
                    table_name=table_name,
                    table_uuid=str(table_info.id),
                    message=e.message,
                    value=start_time
                )
            )
            table_info.status = models.TableMeta.TABLE_STATUS_DELETE_FAILED
            self._table_info_repo.update(context, table_info, ["status"])

            LOG.error("Delete table '%s' with uuid %s failed. %s",
                      table_name,
                      str(table_info.id),
                      str(e))


if __name__ == '__main__':
    from magnetodb import common as mdb_common
    prog_name = os.path.basename(sys.argv[0])
    CONF(project=mdb_common.PROJECT_NAME, prog=prog_name, args=sys.argv[1:])

    logging.setup(mdb_common.PROJECT_NAME)
    notifier.setup()

    LOG.debug('Magnetodb schema processor started')

    transport = messaging.get_transport(cfg.CONF)
    target = messaging.Target(topic='schema',
                              server='magnetodb-async-task-executor')
    endpoints = [
        SchemaEndpoint(),
    ]
    LOG.debug('Creating RPC server..')
    server = messaging.get_rpc_server(transport, target, endpoints,
                                      executor='blocking')
    LOG.debug('Starting...')
    server.start()
    LOG.debug('Waiting...')
    server.wait()
    LOG.debug('Stopped')
