# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from __future__ import unicode_literals, print_function

import unittest
try:
    import mock
except ImportError:
    from unittest import mock

import sys
import argparse

from knack.arguments import ArgumentsContext
from knack.commands import CLICommandsLoader, CommandGroup

from tests.util import DummyCLI, redirect_io, remove_space


def example_handler(arg1, arg2=None, arg3=None):
    """ Short summary here. Long summary here. Still long summary. """
    pass


def example_arg_handler(arg1, opt1, arg2=None, opt2=None, arg3=None,
                        opt3=None, arg4=None, opt4=None, arg5=None, opt5=None):
    pass


class TestCommandExperimental(unittest.TestCase):

    def setUp(self):

        from knack.help_files import helps

        class ExperimentalTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(ExperimentalTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('cmd1', 'example_handler', is_experimental=True)

                with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), is_experimental=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3])
                    c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c'])

                super(ExperimentalTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=ExperimentalTestCommandLoader)

    @redirect_io
    def test_experimental_command_implicitly_execute(self):
        """ Ensure general warning displayed when running command from an experimental parent group. """
        self.cli_ctx.invoke('grp1 cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "Command group 'grp1' is experimental and not covered by customer support. " \
                   "Please use with discretion."
        self.assertIn(remove_space(expected), remove_space(actual))

    @redirect_io
    def test_experimental_command_group_help(self):
        """ Ensure experimental commands appear correctly in group help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('-h'.split())
        actual = self.io.getvalue()
        expected = u"""
Group
    {}

Subgroups:
    grp1 [Experimental] : A group.

Commands:
    cmd1 [Experimental] : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_experimental_command_plain_execute(self):
        """ Ensure general warning displayed when running experimental command. """
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command is experimental and not covered by customer support. Please use with discretion."
        self.assertIn(remove_space(expected), remove_space(actual))


class TestCommandGroupExperimental(unittest.TestCase):

    def setUp(self):

        from knack.help_files import helps

        class ExperimentalTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(ExperimentalTestCommandLoader, self).load_command_table(args)

                with CommandGroup(self, 'group1', '{}#{{}}'.format(__name__), is_experimental=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3])
                    c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c'])

                super(ExperimentalTestCommandLoader, self).load_arguments(command)

        helps['group1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=ExperimentalTestCommandLoader)

    @redirect_io
    def test_experimental_command_group_help_plain(self):
        """ Ensure help warnings appear for experimental command group help. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    cli group1 : A group.
        This command group is experimental and not covered by customer support. Please use with discretion.
Commands:
    cmd1 : Short summary here.

""".format(self.cli_ctx.name)
        self.assertIn(remove_space(expected), remove_space(actual))

    @redirect_io
    def test_experimental_command_implicitly(self):
        """ Ensure help warning displayed for command in experimental because of a experimental parent group. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 cmd1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Command
    {} group1 cmd1 : Short summary here.
        Long summary here. Still long summary.
        Command group 'group1' is experimental and not covered by customer support. Please use with discretion.
""".format(self.cli_ctx.name)
        self.assertIn(remove_space(expected), remove_space(actual))


class TestArgumentExperimental(unittest.TestCase):

    def setUp(self):
        from knack.help_files import helps

        class LoggerAction(argparse.Action):

            def __call__(self, parser, namespace, values, option_string=None):
                print("Side-effect from some original action!", file=sys.stderr)

        class ExperimentalTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(ExperimentalTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('arg-test', 'example_arg_handler')
                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, 'arg-test') as c:
                    c.argument('arg1', help='Arg1', is_experimental=True, action=LoggerAction)

                super(ExperimentalTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=ExperimentalTestCommandLoader)

    @redirect_io
    def test_experimental_arguments_command_help(self):
        """ Ensure experimental arguments appear correctly in command help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test -h'.split())
        actual = self.io.getvalue()
        expected = """
Arguments
    --arg1 [Experimental] [Required] : Arg1.
        Argument '--arg1' is experimental and not covered by customer support. Please use with discretion.
""".format(self.cli_ctx.name)
        self.assertIn(remove_space(expected), remove_space(actual))

    @redirect_io
    def test_experimental_arguments_execute(self):
        """ Ensure deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        experimental_expected = "Argument '--arg1' is experimental and not covered by customer support. " \
                                "Please use with discretion."
        self.assertIn(experimental_expected, actual)

        action_expected = "Side-effect from some original action!"
        self.assertIn(action_expected, actual)


if __name__ == '__main__':
    unittest.main()