# -*- coding: utf-8 -*-
# Copyright (c) 2021 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import annotations

import pytest

from ansible.module_utils.common.arg_spec import ArgumentSpecValidator, ValidationResult

# Each item is id, argument_spec, parameters, expected, valid parameter names
VALID_SPECS = [
    (
        'str-no-type-specified',
        {'name': {}},
        {'name': 'rey'},
        {'name': 'rey'},
        set(('name',)),
    ),
    (
        'str',
        {'name': {'type': 'str'}},
        {'name': 'rey'},
        {'name': 'rey'},
        set(('name',)),
    ),
    (
        'str-convert',
        {'name': {'type': 'str'}},
        {'name': 5},
        {'name': '5'},
        set(('name',)),
    ),
    (
        'list',
        {'packages': {'type': 'list'}},
        {'packages': ['vim', 'python']},
        {'packages': ['vim', 'python']},
        set(('packages',)),
    ),
    (
        'list-comma-string',
        {'packages': {'type': 'list'}},
        {'packages': 'vim,python'},
        {'packages': ['vim', 'python']},
        set(('packages',)),
    ),
    (
        'list-comma-string-space',
        {'packages': {'type': 'list'}},
        {'packages': 'vim, python'},
        {'packages': ['vim', ' python']},
        set(('packages',)),
    ),
    (
        'dict',
        {'user': {'type': 'dict'}},
        {
            'user':
            {
                'first': 'rey',
                'last': 'skywalker',
            }
        },
        {
            'user':
            {
                'first': 'rey',
                'last': 'skywalker',
            }
        },
        set(('user',)),
    ),
    (
        'dict-k=v',
        {'user': {'type': 'dict'}},
        {'user': 'first=rey,last=skywalker'},
        {
            'user':
            {
                'first': 'rey',
                'last': 'skywalker',
            }
        },
        set(('user',)),
    ),
    (
        'dict-k=v-spaces',
        {'user': {'type': 'dict'}},
        {'user': 'first=rey,   last=skywalker'},
        {
            'user':
            {
                'first': 'rey',
                'last': 'skywalker',
            }
        },
        set(('user',)),
    ),
    (
        'bool',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': True,
            'disabled': False,
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-ints',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': 1,
            'disabled': 0,
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-true-false',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': 'true',
            'disabled': 'false',
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-yes-no',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': 'yes',
            'disabled': 'no',
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-y-n',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': 'y',
            'disabled': 'n',
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-on-off',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': 'on',
            'disabled': 'off',
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-1-0',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': '1',
            'disabled': '0',
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'bool-float',
        {
            'enabled': {'type': 'bool'},
            'disabled': {'type': 'bool'},
        },
        {
            'enabled': 1.0,
            'disabled': 0.0,
        },
        {
            'enabled': True,
            'disabled': False,
        },
        set(('enabled', 'disabled')),
    ),
    (
        'float',
        {'digit': {'type': 'float'}},
        {'digit': 3.14159},
        {'digit': 3.14159},
        set(('digit',)),
    ),
    (
        'float-str',
        {'digit': {'type': 'float'}},
        {'digit': '3.14159'},
        {'digit': 3.14159},
        set(('digit',)),
    ),
    (
        'path',
        {'path': {'type': 'path'}},
        {'path': '~/bin'},
        {'path': '/home/ansible/bin'},
        set(('path',)),
    ),
    (
        'raw',
        {'raw': {'type': 'raw'}},
        {'raw': 0x644},
        {'raw': 0x644},
        set(('raw',)),
    ),
    (
        'bytes',
        {'bytes': {'type': 'bytes'}},
        {'bytes': '2K'},
        {'bytes': 2048},
        set(('bytes',)),
    ),
    (
        'bits',
        {'bits': {'type': 'bits'}},
        {'bits': '1Mb'},
        {'bits': 1048576},
        set(('bits',)),
    ),
    (
        'jsonarg',
        {'some_json': {'type': 'jsonarg'}},
        {'some_json': '{"users": {"bob": {"role": "accountant"}}}'},
        {'some_json': '{"users": {"bob": {"role": "accountant"}}}'},
        set(('some_json',)),
    ),
    (
        'jsonarg-list',
        {'some_json': {'type': 'jsonarg'}},
        {'some_json': ['one', 'two']},
        {'some_json': '["one", "two"]'},
        set(('some_json',)),
    ),
    (
        'jsonarg-dict',
        {'some_json': {'type': 'jsonarg'}},
        {'some_json': {"users": {"bob": {"role": "accountant"}}}},
        {'some_json': '{"users": {"bob": {"role": "accountant"}}}'},
        set(('some_json',)),
    ),
    (
        'defaults',
        {'param': {'default': 'DEFAULT'}},
        {},
        {'param': 'DEFAULT'},
        set(('param',)),
    ),
    (
        'elements',
        {'numbers': {'type': 'list', 'elements': 'int'}},
        {'numbers': [55, 33, 34, '22']},
        {'numbers': [55, 33, 34, 22]},
        set(('numbers',)),
    ),
    (
        'aliases',
        {'src': {'aliases': ['path', 'source']}},
        {'src': '/tmp'},
        {'src': '/tmp'},
        set(('src (path, source)',)),
    )
]


@pytest.mark.parametrize(
    ('arg_spec', 'parameters', 'expected', 'valid_params'),
    (i[1:] for i in VALID_SPECS),
    ids=[i[0] for i in VALID_SPECS]
)
def test_valid_spec(arg_spec, parameters, expected, valid_params, mocker):
    mocker.patch('ansible.module_utils.common.validation.os.path.expanduser', return_value='/home/ansible/bin')
    mocker.patch('ansible.module_utils.common.validation.os.path.expandvars', return_value='/home/ansible/bin')

    v = ArgumentSpecValidator(arg_spec)
    result = v.validate(parameters)

    assert isinstance(result, ValidationResult)
    assert result.validated_parameters == expected
    assert result.unsupported_parameters == set()
    assert result.error_messages == []
    assert v._valid_parameter_names == valid_params

    # Again to check caching
    assert v._valid_parameter_names == valid_params
