# 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.

"""
CLI interface for senlin management.
"""

import sys

from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import timeutils

from senlin.common import config
from senlin.common import context
from senlin.common.i18n import _
from senlin.db import api
from senlin.objects import service as service_obj

CONF = cfg.CONF


def do_db_version():
    """Print database's current migration level."""
    print(api.db_version(api.get_engine()))


def do_db_sync():
    """Place a database under migration control and upgrade.

    DB is created first if necessary.
    """
    api.db_sync(api.get_engine(), CONF.command.version)


def do_event_purge():
    """Purge the specified event records in senlin's database."""
    if CONF.command.age < 0:
        print(_("Age must be a positive integer."))
        return
    api.event_purge(api.get_engine(),
                    CONF.command.project_id,
                    CONF.command.granularity,
                    CONF.command.age)


def do_action_purge():
    """Purge the specified action records in senlin's database."""
    age = CONF.command.age

    if age < 0:
        print(_("Age must be a positive integer."))
        return

    if CONF.command.granularity == 'days':
        age = age * 86400
    elif CONF.command.granularity == 'hours':
        age = age * 3600
    elif CONF.command.granularity == 'minutes':
        age = age * 60

    if age < CONF.default_action_timeout:
        print(_("Age must be greater than the default action timeout."))
        return
    api.action_purge(api.get_engine(),
                     CONF.command.project_id,
                     CONF.command.granularity,
                     CONF.command.age)


class ServiceManageCommand(object):
    def __init__(self):
        self.ctx = context.get_admin_context()

    def _format_service(self, service):
        if service is None:
            return

        status = 'up'
        CONF.import_opt('periodic_interval', 'senlin.conf')
        max_interval = 2 * CONF.periodic_interval
        if timeutils.is_older_than(service.updated_at, max_interval):
            status = 'down'

        result = {
            'service_id': service.id,
            'binary': service.binary,
            'host': service.host,
            'topic': service.topic,
            'created_at': service.created_at,
            'updated_at': service.updated_at,
            'status': status
        }
        return result

    def service_list(self):
        services = [self._format_service(service)
                    for service in service_obj.Service.get_all(self.ctx)]

        print_format = "%-36s %-24s %-16s %-16s %-10s %-24s %-24s"
        print(print_format % (_('Service ID'),
                              _('Host'),
                              _('Binary'),
                              _('Topic'),
                              _('Status'),
                              _('Created At'),
                              _('Updated At')))

        for svc in services:
            print(print_format % (svc['service_id'],
                                  svc['host'],
                                  svc['binary'],
                                  svc['topic'],
                                  svc['status'],
                                  svc['created_at'],
                                  svc['updated_at']))

    def service_clean(self):
        for service in service_obj.Service.get_all(self.ctx):
            svc = self._format_service(service)
            if svc['status'] == 'down':
                print(_('Dead service %s is removed.') % svc['service_id'])
                service_obj.Service.delete(svc['service_id'])

    @staticmethod
    def add_service_parsers(subparsers):
        service_parser = subparsers.add_parser('service')
        service_parser.set_defaults(command_object=ServiceManageCommand)
        service_subparsers = service_parser.add_subparsers(dest='action')
        list_parser = service_subparsers.add_parser('list')
        list_parser.set_defaults(func=ServiceManageCommand().service_list)
        remove_parser = service_subparsers.add_parser('clean')
        remove_parser.set_defaults(func=ServiceManageCommand().service_clean)


def add_command_parsers(subparsers):
    parser = subparsers.add_parser('db_version')
    parser.set_defaults(func=do_db_version)

    parser = subparsers.add_parser('db_sync')
    parser.set_defaults(func=do_db_sync)
    ServiceManageCommand.add_service_parsers(subparsers)
    parser.add_argument('version', nargs='?')
    parser.add_argument('current_version', nargs='?')

    parser = subparsers.add_parser('event_purge')
    parser.set_defaults(func=do_event_purge)
    parser.add_argument('-p',
                        '--project-id',
                        nargs='?',
                        metavar='<project1;project2...>',
                        help=_("Purge event records with specified project. "
                               "This can be specified multiple times, or once "
                               "with parameters separated by semicolon."),
                        action='append')
    parser.add_argument('-g',
                        '--granularity',
                        default='days',
                        choices=['days', 'hours', 'minutes', 'seconds'],
                        help=_("Purge event records which were created in the "
                               "specified time period. The time is specified "
                               "by age and granularity, whose value must be "
                               "one of 'days', 'hours', 'minutes' or "
                               "'seconds' (default)."))
    parser.add_argument('age',
                        type=int,
                        default=30,
                        help=_("Purge event records which were created in the "
                               "specified time period. The time is specified "
                               "by age and granularity. For example, "
                               "granularity=hours and age=2 means purging "
                               "events created two hours ago. Defaults to "
                               "30."))

    parser = subparsers.add_parser('action_purge')
    parser.set_defaults(func=do_action_purge)
    parser.add_argument('-p',
                        '--project-id',
                        nargs='?',
                        metavar='<project1;project2...>',
                        help=_("Purge action records with specified project. "
                               "This can be specified multiple times, or once "
                               "with parameters separated by semicolon."),
                        action='append')
    parser.add_argument('-g',
                        '--granularity',
                        default='days',
                        choices=['days', 'hours', 'minutes', 'seconds'],
                        help=_("Purge action records which were created in "
                               "the specified time period. The time is "
                               "specified by age and granularity, whose value "
                               "must be one of 'days', 'hours', 'minutes' or "
                               "'seconds' (default)."))
    parser.add_argument('age',
                        type=int,
                        default=30,
                        help=_("Purge action records which were created in "
                               "the specified time period. The time is "
                               "specified by age and granularity. For "
                               "example, granularity=hours and age=2 means "
                               "purging actions created two hours ago. "
                               "Defaults to 30."))


command_opt = cfg.SubCommandOpt('command',
                                title='Commands',
                                help=_('Show available commands.'),
                                handler=add_command_parsers)


def main():
    try:
        CONF.register_cli_opt(command_opt)
        default_config_files = cfg.find_config_files('senlin', 'senlin-manage')
        config.parse_args(sys.argv, 'senlin-manage', default_config_files)
        logging.setup(CONF, 'senlin-manage')
    except RuntimeError as e:
        sys.exit("ERROR: %s" % e)

    try:
        CONF.command.func()
    except Exception as e:
        sys.exit("ERROR: %s" % e)
