"""
The command line: what reaches the pipeline, and what is refused before it does.

The two modes - one variable named on the command line, or a loop prompting for
them - had drifted apart, so most of this is about keeping them together.
"""

import inspect

import pytest

from dispnc.cli import _plot_options, _validate, build_parser
from dispnc.pipeline import plot_variable


# What the call sites supply themselves, because it is genuinely different
# between the two modes: where the figure goes, which slice it shows, and
# whether it may stop and ask.
PER_CALL = {'src', 'report', 'varname', 'output_path', 'extra_indices', 'interactive'}


def test_every_option_the_pipeline_takes_reaches_both_modes():
    """
    The interactive loop used to forward eleven of plot_variable's arguments and
    drop ten - --norm, --vmin, --vmax, --x-dim, --show-polar, --show-3d,
    --anomaly, --reduce, --stats and --stats-only - while the README promised
    they were honoured there.

    This is the test that fails the day someone adds an option to the pipeline
    and forgets the forwarder, which is exactly how that happened.
    """
    args = build_parser().parse_args(['f.nc', '-v', 'tsurf'])
    expected = set(inspect.signature(plot_variable).parameters) - PER_CALL
    assert set(_plot_options(args)) == expected


def test_the_options_survive_with_nothing_parsed_at_all():
    """
    Interactive mode can be reached with args=None, so the forwarder has to be
    as tolerant as globe_options_from already is.
    """
    options = _plot_options(None)
    assert options['colormap'] == 'auto'
    assert options['remap'] is True
    assert options['stats'] is False


@pytest.mark.parametrize('argv, key, expected', [
    (['--norm', 'log'], 'norm', 'log'),
    (['--vmin', '1'], 'vmin', 1.0),
    (['--x-dim', 'lon'], 'x_dim', 'lon'),
    (['--show-polar'], 'show_polar', True),
    (['--anomaly', 'time'], 'anomaly', 'time'),
    (['--reduce', 'mean:lon'], 'reduce', ['mean:lon']),
    (['--stats'], 'stats', True),
    (['--no-remap'], 'remap', False),
])
def test_a_style_option_is_read_off_the_arguments(argv, key, expected):
    args = build_parser().parse_args(['f.nc', '-v', 'tsurf'] + argv)
    assert _plot_options(args)[key] == expected


def test_an_inverted_colour_scale_is_refused_rather_than_swapped(capsys):
    """
    Exit 2, the documented status for bad arguments. Swapping the pair would be
    the quiet correction this tool avoids: an inverted scale is a plausible
    typo, not a plausible intent.
    """
    parser = build_parser()
    args = parser.parse_args(['f.nc', '-v', 'tsurf', '--vmin', '300', '--vmax', '100'])
    with pytest.raises(SystemExit) as exit:
        _validate(args, parser)
    assert exit.value.code == 2
    said = capsys.readouterr().err
    assert '--vmin' in said and '--vmax' in said


def test_equal_limits_are_refused_too():
    parser = build_parser()
    args = parser.parse_args(['f.nc', '-v', 'tsurf', '--vmin', '5', '--vmax', '5'])
    with pytest.raises(SystemExit):
        _validate(args, parser)


@pytest.mark.parametrize('argv', [
    ['--vmin', '100', '--vmax', '300'],
    ['--vmin', '100'],
    ['--vmax', '300'],
    [],
])
def test_a_usable_pair_of_limits_passes(argv):
    parser = build_parser()
    _validate(parser.parse_args(['f.nc', '-v', 'tsurf'] + argv), parser)


@pytest.mark.parametrize('value, expected', [
    ('8,6', (8.0, 6.0)),
    (' 12 , 5 ', (12.0, 5.0)),
    ('6.5,4.25', (6.5, 4.25)),
])
def test_a_figure_size_is_two_numbers_in_inches(value, expected):
    parser = build_parser()
    args = parser.parse_args(['f.nc', '-v', 'tsurf', '--figsize', value])
    _validate(args, parser)
    assert args.figsize == expected


@pytest.mark.parametrize('value', ['8', 'a,b', '8,6,4', '-3,6', '0,6', ''])
def test_a_figure_size_that_is_not_two_positive_numbers_is_refused(value):
    """
    A comma, matching --sun LON,LAT. One spelling per idea, so '8x6' is not
    also accepted.
    """
    parser = build_parser()
    args = parser.parse_args(['f.nc', '-v', 'tsurf', f'--figsize={value}'])
    with pytest.raises(SystemExit) as exit:
        _validate(args, parser)
    assert exit.value.code == 2


def test_a_dpi_has_to_be_positive():
    parser = build_parser()
    args = parser.parse_args(['f.nc', '-v', 'tsurf', '--dpi=-100'])
    with pytest.raises(SystemExit):
        _validate(args, parser)
