# Copyright 2018 Red Hat
#
#    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.
from __future__ import absolute_import

import sys

from oslo_log import log
import six

import tobiko
from tobiko.cmd import base

LOG = log.getLogger(__name__)


class FixtureUtil(base.TobikoCMD):

    def get_parser(self):
        parser = super(FixtureUtil, self).get_parser()

        subparsers = parser.add_subparsers(
            title='subcommands', description='fixture operation')

        subcommand_parser = subparsers.add_parser(
            'help', help=('show this help message'))
        subcommand_parser.set_defaults(subcommand='help')

        for subcommand_name in ['cleanup', 'list', 'setup']:
            subcommand_parser = subparsers.add_parser(
                subcommand_name, help=(subcommand_name + ' fixtures'))
            subcommand_parser.set_defaults(subcommand=subcommand_name)
            subcommand_parser.add_argument(
                '--config', '-c',
                default='.stestr.conf',
                help=("Set a stestr config file to use with this command. "
                      "If one isn't specified then .stestr.conf in the "
                      "directory that a command is running from is used"))
            subcommand_parser.add_argument(
                '--repo-type', '-r',
                choices=['file'],
                default='file',
                help=("Select the repo backend to use"))
            subcommand_parser.add_argument(
                '--repo-url', '-u',
                help=("Set the repo url to use. An acceptable value for "
                      "this depends on the repository type used."))
            subcommand_parser.add_argument(
                '--test-path', '-t',
                help=("Set the test path to use for unittest discovery. If "
                      "both this and the corresponding config file option "
                      "are set, this value will be used."))
            subcommand_parser.add_argument(
                '--top-dir',
                help=("Set the top dir to use for unittest discovery. If "
                      "both this and the corresponding config file option "
                      "are set, this value will be used."))
            subcommand_parser.add_argument(
                '--group-regex', '--group_regex', '-g',
                help=("Set a group regex to use for grouping tests together "
                      "in the stestr scheduler. If both this and the "
                      "corresponding config file option are set this value "
                      "will be used."))
            subcommand_parser.add_argument(
                '--blacklist-file', '-b',
                help=("Path to a blacklist file, this file contains a "
                      "separate regex exclude on each newline."))
            subcommand_parser.add_argument(
                '--whitelist-file', '-w',
                help=("Path to a whitelist file, this file contains a "
                      "separate regex on each newline."))
            subcommand_parser.add_argument(
                '--black-regex', '-B',
                help=("Test rejection regex. If a test cases name matches "
                      "on re.search() operation , it will be removed from "
                      "the final test list. Effectively the black-regexp is "
                      "added to black regexp list, but you do need to edit "
                      "a file. The black filtering happens after the "
                      "initial white selection, which by default is "
                      "everything."))
            subcommand_parser.add_argument(
                'filters',
                nargs='*',
                help=("A list of string regex filters to initially apply "
                      "on the test list. Tests that match any of the "
                      "regexes will be used. (assuming any other filtering "
                      "specified also uses it)."))
        return parser

    def execute(self):
        action = self.args.subcommand or 'list'
        if action == 'help':
            return self.show_help()
        if action == 'list':
            return self.list_fixtures()
        elif action == 'setup':
            return self.setup_fixtures()
        elif action == 'cleanup':
            return self.cleanup_fixtures()

    def show_help(self, stream=None):
        stream = stream or sys.stdout
        self.parser.print_help(stream)

    def discover_testcases(self):
        args = self.args
        return tobiko.discover_test_cases(
            config=args.config,
            repo_type=args.repo_type,
            repo_url=args.repo_url,
            test_path=args.test_path,
            top_dir=args.top_dir,
            group_regex=args.group_regex,
            blacklist_file=args.blacklist_file,
            whitelist_file=args.whitelist_file,
            black_regex=args.black_regex,
            filters=args.filters)

    def list_fixtures(self, stream=None):
        stream = stream or sys.stdout
        test_cases = self.discover_testcases()
        fixtures_names = tobiko.list_required_fixtures(test_cases)
        if fixtures_names:
            output = '\n'.join(fixtures_names) + '\n'
            if six.PY2:
                output = output.decode()
            stream.write(output)

    def setup_fixtures(self, stream=None):
        stream = stream or sys.stdout
        test_cases = self.discover_testcases()
        success = True
        for fixture_name in tobiko.list_required_fixtures(test_cases):
            output = fixture_name + '\n'
            if six.PY2:
                output = output.decode()
            stream.write(output)
            try:
                tobiko.setup_fixture(fixture_name)
            except Exception:
                LOG.exception('Fixture %r setup failed', fixture_name)
                success = False
        if not success:
            sys.exit(1)

    def cleanup_fixtures(self, stream=None):
        stream = stream or sys.stdout
        test_cases = self.discover_testcases()
        success = True
        for fixture_name in tobiko.list_required_fixtures(test_cases):
            output = fixture_name + '\n'
            if six.PY2:
                output = output.decode()
            stream.write(output)
            try:
                tobiko.cleanup_fixture(fixture_name)
            except Exception:
                LOG.exception('Fixture %r cleanup failed', fixture_name)
                success = False
        if not success:
            sys.exit(1)


def main():
    """Create CLI main entry."""
    fixture_util = FixtureUtil()
    fixture_util.set_stream_handler_logging_level()
    fixture_util.execute()


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