#!/opt/alt/python35/bin/python3

import argparse
import inspect
import os
import sys

import yaml

from restore_infected import backup_backends, helpers, restore
from restore_infected.backup_backends_lib import BackendNonApplicableError


def action(func):
    spec = inspect.getfullargspec(func)

    def wrapper(extra_args, parser, **kwargs):
        if 'extra_args' not in spec.args and extra_args:
            raise parser.error('unrecognized arguments: ' +
                               ' '.join(extra_args))
        func(extra_args=extra_args, parser=parser, **kwargs)

    return wrapper


@action
def action_init(backend, extra_args, parser, **_):
    args, kwargs = helpers.process_args(extra_args)

    missing, unknown = helpers.validate_params(backend.init, args, kwargs)
    if missing:
        raise parser.error('init arguments required: ' + ' '.join(missing))
    if unknown:
        raise parser.error('init unknown keys: ' + ' '.join(unknown))

    backend.init(*args, **kwargs)


@action
def action_list(backend, until, **_):
    backups = backend.backups(until)

    if backups:
        print(os.linesep.join([str(b) for b in backups]))
    else:
        sys.exit('No backups found')


@action
def action_restore(backend, files, until, **_):
    success, failed = restore.restore_infected(backend, files, until)

    if success:
        print('Restore success:')
        print(os.linesep.join(success))

    if failed:
        print('Restore failed:')
        print(os.linesep.join(failed))


@action
def action_cleanup(backend, **_):
    restore.cleanup(backend)


@action
def action_info(backend, **_):
    info = backend.info()
    if info:
        print(yaml.dump(info, default_flow_style=False))


@action
def action_extra(backend, extra_action, extra_args, parser, **_):
    extra_func = getattr(backend, extra_action, None)
    if not getattr(extra_func, 'extra', False):
        raise parser.error('invalid extra action: {0}'.format(extra_action))
    result = extra_func(*extra_args)
    if result:
        print(result)


# @action
def action_none(parser, subparsers, **_):
    raise parser.error('one of the action must be specified: ' +
                       ' '.join(subparsers.choices))


def main():
    parser = argparse.ArgumentParser()
    parser.set_defaults(action=action_none)
    parser.add_argument('backend', metavar='BACKEND',
                        type=backup_backends.backend,
                        help='Backup backend to use')
    subparsers = parser.add_subparsers()

    parser_init = subparsers.add_parser('init')
    parser_init.set_defaults(action=action_init, action_repr='Init')

    parser_list = subparsers.add_parser('list')
    parser_list.set_defaults(action=action_list,
                             action_repr='Getting backup list')
    parser_list.add_argument('-u', '--until', default=None, metavar='DATE',
                             type=helpers.DateTime,
                             help='Dig backups not earlier this date')

    parser_restore = subparsers.add_parser('restore')
    parser_restore.set_defaults(action=action_restore, action_repr='Restore')
    parser_restore.add_argument('files', nargs='+', metavar='FILE',
                                help='List files to restore')
    parser_restore.add_argument('-u', '--until', default=None, metavar='DATE',
                                type=helpers.DateTime,
                                help='Dig backups not earlier this date')

    parser_cleanup = subparsers.add_parser('cleanup')
    parser_cleanup.set_defaults(action=action_cleanup, action_repr='Cleanup')

    parser_info = subparsers.add_parser('info')
    parser_info.set_defaults(action=action_info, action_repr='Info')

    parser_extra = subparsers.add_parser('extra')
    parser_extra.set_defaults(action=action_extra, action_repr='Extra actions')
    parser_extra.add_argument('extra_action', help='Extra action for backend')

    try:
        args, extra_args = parser.parse_known_args()
    except BackendNonApplicableError as e:
        sys.exit(e)
    args.extra_args = extra_args
    args.parser = parser
    args.subparsers = subparsers

    try:
        args.action(**args.__dict__)
    except helpers.ActionError as e:
        e.message and print(e.message, file=sys.stderr)
        sys.exit(e.code)
    except Exception as e:
        sys.exit(args.action_repr + ' error: ' + str(e))


if __name__ == '__main__':
    main()
