import sys
import click
import pytest


def debug():
    click.echo('%s=%s' % (
        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 %s' % format)

    @cli.command('bdist')
    @click.option('--format')
    def bdist(format):
        click.echo('bdist called %s' % 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 %s' % format)

    @cli.command('bdist')
    @click.argument('format')
    def bdist(format):
        click.echo('bdist called %s' % 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:%s' % 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=',
    ]
