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

"""Migrate introspected data between Swift and database."""

import sys

from oslo_config import cfg
from oslo_log import log
from oslo_utils import encodeutils

from ironic_inspector.common.i18n import _
from ironic_inspector.conf import opts
from ironic_inspector import node_cache
from ironic_inspector.plugins import base as plugins_base
from ironic_inspector import utils

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

_AVAILABLE_STORAGES = [('database', _('The database storage backend')),
                       ('swift', _('The Swift storage backend'))]
_OPTS = [
    cfg.StrOpt('from',
               dest='source_storage',
               required=True,
               choices=_AVAILABLE_STORAGES,
               help=_('The source storage where the introspected data will be '
                      'read from.')),
    cfg.StrOpt('to',
               dest='target_storage',
               required=True,
               choices=_AVAILABLE_STORAGES,
               help=_('The target storage where the introspected data will be '
                      'saved to.'))
]

# Migration result
RESULT_NOCONTENT = 'no content'
RESULT_FAILED = 'failed'
RESULT_SUCCESS = 'success'


def _setup_logger(args=None):
    args = [] if args is None else args
    log.register_options(CONF)
    opts.set_config_defaults()
    opts.parse_args(args)
    log.setup(CONF, 'ironic_inspector')


class MigrationTool(object):

    def _migrate_one(self, node, processed):
        LOG.debug('Starting to migrate introspection data for node '
                  '%(node)s (processed %(processed)s)',
                  {'node': node.uuid, 'processed': processed})
        try:
            data = self.ext_src.get(node.uuid, processed=processed,
                                    get_json=True)
            if not data:
                return RESULT_NOCONTENT
            self.ext_tgt.save(node.uuid, data, processed=processed)
        except Exception as e:
            LOG.error('Migrate introspection data failed for node '
                      '%(node)s (processed %(processed)s), error: '
                      '%(error)s', {'node': node.uuid, 'processed': processed,
                                    'error': e})
            return RESULT_FAILED

        return RESULT_SUCCESS

    def main(self):
        CONF.register_cli_opts(_OPTS)
        _setup_logger(sys.argv[1:])

        if CONF.source_storage == CONF.target_storage:
            raise utils.Error(_('Source and destination can not be the same.'))

        introspection_data_manager = plugins_base.introspection_data_manager()
        self.ext_src = introspection_data_manager[CONF.source_storage].obj
        self.ext_tgt = introspection_data_manager[CONF.target_storage].obj

        nodes = node_cache.get_node_list()
        migration_list = [(n, p) for n in nodes for p in [True, False]]
        failed_records = []
        for node, processed in migration_list:
            result = self._migrate_one(node, processed)
            if result == RESULT_FAILED:
                failed_records.append((node.uuid, processed))

        msg = ('Finished introspection data migration, total records: %d. '
               % len(migration_list))
        if failed_records:
            msg += 'Failed to migrate:\n' + '\n'.join([
                '%s(processed=%s)' % (record[0], record[1])
                for record in failed_records])
        elif len(migration_list) > 0:
            msg += 'all records are migrated successfully.'
        print(msg)


def main():

    try:
        MigrationTool().main()
    except KeyboardInterrupt:
        print(_("... terminating migration tool"), file=sys.stderr)
        return 130
    except Exception as e:
        print(encodeutils.safe_encode(str(e)), file=sys.stderr)
        return 1


if __name__ == '__main__':
    sys.exit(main())
