import argparse
import contextlib
import os
import sys
import unittest
from configparser import ConfigParser
from unittest.mock import patch, MagicMock, call

from setuptools.dist import Distribution

from green import command
from green.config import StoreOpt


class TestCommand(unittest.TestCase):
    @contextlib.contextmanager
    def environ(self, setup_cfg=None, *args, **variables):
        args = ["green"] + list(args)

        if setup_cfg is not None:
            parser = ConfigParser()
            parser.add_section("green")
            for k, v in setup_cfg.items():
                parser.set("green", k, str(v))
            with open("setup.cfg", "w") as f:
                parser.write(f)

        yield Distribution(
            {"script_name": "setup.py", "script_args": args or ["green"]}
        )

        # finally:
        if os.path.isfile("setup.cfg"):
            os.remove("setup.cfg")

    def test_get_user_options(self):
        # Check the user options contain some of the
        # actual command line options
        options = command.get_user_options()

        self.assertIn(
            ("options", None, "Output all options.  Used by bash- and zsh-completion."),
            options,
        )

        self.assertIn(
            ("file-pattern=", "p", "Pattern to match test files. Default is test*.py"),
            options,
        )

    def test_get_user_options_setup_py(self):
        """
        When get_user_options() is called by 'python setup.py --help-commands',
        it returns [] early and doesn't crash.
        """
        sys.argv.append("--help-commands")
        self.addCleanup(lambda: sys.argv.pop())

        self.assertEqual(command.get_user_options(), [])

    @patch("green.command.parseArguments")
    def test_get_user_options_dynamic(self, parseArguments):
        # Check the user options are generated after
        # the command line options created in green.cmdline.parseArguments
        store_opt = StoreOpt()
        argparser = argparse.ArgumentParser()
        store_opt(argparser.add_argument("-s", "--something", help="Something"))
        store_opt(argparser.add_argument("--else", help="Else"))
        store_opt(
            argparser.add_argument("-a", "--again", action="store_true", help="Again")
        )

        args = argparser.parse_args([])
        args.parser = argparser
        args.store_opt = store_opt
        parseArguments.return_value = args

        options = command.get_user_options()

        self.assertEqual(
            options,
            [
                ("something=", "s", "Something"),
                ("else=", None, "Else"),
                ("again", "a", "Again"),
            ],
        )

    def test_initialize_options(self):
        d = Distribution({"script_name": "setup.py", "script_args": ["green"]})

        cmd = command.green(d)
        for attr in ["completion_file", "clear_omit", "debug", "processes"]:
            self.assertTrue(hasattr(cmd, attr), attr)

    @patch("green.command.main", return_value=0)
    def test_run(self, main):
        d = Distribution(
            {
                "script_name": "setup.py",
                "script_args": ["green"],
                "test_suite": "green.test.test_version",
            }
        )

        cmd = command.green(d)
        cmd.run()
        main.assert_called_once_with(["green.test.test_version"])

    @patch("green.command.main", return_value=125)
    def test_run_exits(self, main):
        d = Distribution({"script_name": "setup.py", "script_args": ["green"]})

        cmd = command.green(d)
        with self.assertRaises(SystemExit) as se:
            cmd.run()
        self.assertEqual(se.exception.code, 125)

    @patch("green.command.main", return_value=0)
    def test_requires(self, main):
        d = Distribution(
            {
                "script_name": "setup.py",
                "script_args": ["green"],
                "install_requires": ["six"],
                "tests_require": ["mock", "unittest2"],
            }
        )
        d.fetch_build_eggs = MagicMock()
        cmd = command.green(d)
        cmd.run()

        d.fetch_build_eggs.assert_has_calls(
            [
                call(["six"]),
                call(["mock", "unittest2"]),
            ]
        )
