import sys

import pytest

import asyncclick as click


def debug():
    click.echo(
        "{}={}".format(
            sys._getframe(1).f_code.co_name, "|".join(click.get_current_context().args)
        )
    )


def test_basic_chaining(runner):
    @click.group(chain=True)
    def cli():
        pass

    @cli.command("sdist")
    def sdist():
        click.echo("sdist called")

    @cli.command("bdist")
    def bdist():
        click.echo("bdist called")

    result = runner.invoke(cli, ["bdist", "sdist", "bdist"])
    assert not result.exception
    assert result.output.splitlines() == [
        "bdist called",
        "sdist called",
        "bdist called",
    ]


def test_chaining_help(runner):
    @click.group(chain=True)
    def cli():
        """ROOT HELP"""
        pass

    @cli.command("sdist")
    def sdist():
        """SDIST HELP"""
        click.echo("sdist called")

    @cli.command("bdist")
    def bdist():
        """BDIST HELP"""
        click.echo("bdist called")

    result = runner.invoke(cli, ["--help"])
    assert not result.exception
    assert "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." in result.output
    assert "ROOT HELP" in result.output

    result = runner.invoke(cli, ["sdist", "--help"])
    assert not result.exception
    assert "SDIST HELP" in result.output

    result = runner.invoke(cli, ["bdist", "--help"])
    assert not result.exception
    assert "BDIST HELP" in result.output

    result = runner.invoke(cli, ["bdist", "sdist", "--help"])
    assert not result.exception
    assert "SDIST HELP" in result.output


def test_chaining_with_options(runner):
    @click.group(chain=True)
    def cli():
        pass

    @cli.command("sdist")
    @click.option("--format")
    def sdist(format):
        click.echo("sdist called {}".format(format))

    @cli.command("bdist")
    @click.option("--format")
    def bdist(format):
        click.echo("bdist called {}".format(format))

    result = runner.invoke(cli, ["bdist", "--format=1", "sdist", "--format=2"])
    assert not result.exception
    assert result.output.splitlines() == ["bdist called 1", "sdist called 2"]


def test_chaining_with_arguments(runner):
    @click.group(chain=True)
    def cli():
        pass

    @cli.command("sdist")
    @click.argument("format")
    def sdist(format):
        click.echo("sdist called {}".format(format))

    @cli.command("bdist")
    @click.argument("format")
    def bdist(format):
        click.echo("bdist called {}".format(format))

    result = runner.invoke(cli, ["bdist", "1", "sdist", "2"])
    assert not result.exception
    assert result.output.splitlines() == ["bdist called 1", "sdist called 2"]


def test_pipeline(runner):
    @click.group(chain=True, invoke_without_command=True)
    @click.option("-i", "--input", type=click.File("r"))
    def cli(input):
        pass

    @cli.resultcallback()
    def process_pipeline(processors, input):
        iterator = (x.rstrip("\r\n") for x in input)
        for processor in processors:
            iterator = processor(iterator)
        for item in iterator:
            click.echo(item)

    @cli.command("uppercase")
    def make_uppercase():
        def processor(iterator):
            for line in iterator:
                yield line.upper()

        return processor

    @cli.command("strip")
    def make_strip():
        def processor(iterator):
            for line in iterator:
                yield line.strip()

        return processor

    result = runner.invoke(cli, ["-i", "-"], input="foo\nbar")
    assert not result.exception
    assert result.output.splitlines() == ["foo", "bar"]

    result = runner.invoke(cli, ["-i", "-", "strip"], input="foo \n bar")
    assert not result.exception
    assert result.output.splitlines() == ["foo", "bar"]

    result = runner.invoke(cli, ["-i", "-", "strip", "uppercase"], input="foo \n bar")
    assert not result.exception
    assert result.output.splitlines() == ["FOO", "BAR"]


def test_args_and_chain(runner):
    @click.group(chain=True)
    def cli():
        debug()

    @cli.command()
    def a():
        debug()

    @cli.command()
    def b():
        debug()

    @cli.command()
    def c():
        debug()

    result = runner.invoke(cli, ["a", "b", "c"])
    assert not result.exception
    assert result.output.splitlines() == ["cli=", "a=", "b=", "c="]


def test_multicommand_arg_behavior(runner):
    with pytest.raises(RuntimeError):

        @click.group(chain=True)
        @click.argument("forbidden", required=False)
        def bad_cli():
            pass

    with pytest.raises(RuntimeError):

        @click.group(chain=True)
        @click.argument("forbidden", nargs=-1)
        def bad_cli2():
            pass

    @click.group(chain=True)
    @click.argument("arg")
    def cli(arg):
        click.echo("cli:{}".format(arg))

    @cli.command()
    def a():
        click.echo("a")

    result = runner.invoke(cli, ["foo", "a"])
    assert not result.exception
    assert result.output.splitlines() == ["cli:foo", "a"]


@pytest.mark.xfail
def test_multicommand_chaining(runner):
    @click.group(chain=True)
    def cli():
        debug()

    @cli.group()
    def l1a():
        debug()

    @l1a.command()
    def l2a():
        debug()

    @l1a.command()
    def l2b():
        debug()

    @cli.command()
    def l1b():
        debug()

    result = runner.invoke(cli, ["l1a", "l2a", "l1b"])
    assert not result.exception
    assert result.output.splitlines() == ["cli=", "l1a=", "l2a=", "l1b="]
