# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type

import ssl
import sys
import pytest

pyvmomi = pytest.importorskip('pyVmomi')

from units.compat import mock

import ansible.module_utils.vmware as vmware_module_utils


test_data = [
    (
        dict(
            username='Administrator@vsphere.local',
            password='Esxi@123$%',
            hostname=False,
            validate_certs=False,
        ),
        "Hostname parameter is missing. Please specify this parameter in task or"
        " export environment variable like 'export VMWARE_HOST=ESXI_HOSTNAME'"
    ),
    (
        dict(
            username=False,
            password='Esxi@123$%',
            hostname='esxi1',
            validate_certs=False,
        ),
        "Username parameter is missing. Please specify this parameter in task or"
        " export environment variable like 'export VMWARE_USER=ESXI_USERNAME'"
    ),
    (
        dict(
            username='Administrator@vsphere.local',
            password=False,
            hostname='esxi1',
            validate_certs=False,
        ),
        "Password parameter is missing. Please specify this parameter in task or"
        " export environment variable like 'export VMWARE_PASSWORD=ESXI_PASSWORD'"
    ),
    (
        dict(
            username='Administrator@vsphere.local',
            password='Esxi@123$%',
            hostname='esxi1',
            validate_certs=True,
        ),
        "Unknown error while connecting to vCenter or ESXi API at esxi1:443"
    ),
    (
        dict(
            username='Administrator@vsphere.local',
            password='Esxi@123$%',
            hostname='esxi1',
            proxy_host='myproxyserver.com',
            proxy_port=80,
            validate_certs=False,
        ),
        " [proxy: myproxyserver.com:80]"
    ),
]

test_ids = [
    'hostname',
    'username',
    'password',
    'validate_certs',
    'valid_http_proxy',
]


class FailJsonException(BaseException):
    pass


@pytest.fixture
def fake_ansible_module():
    ret = mock.Mock()
    ret.params = test_data[3][0]
    ret.tmpdir = None
    ret.fail_json.side_effect = FailJsonException()
    return ret


def fake_connect_to_api(module, return_si=None):
    return None, mock.Mock(),


testdata = [
    ('HAS_PYVMOMI', 'PyVmomi'),
    ('HAS_REQUESTS', 'requests'),
]


@pytest.mark.parametrize("key,libname", testdata)
def test_lib_loading_failure(monkeypatch, fake_ansible_module, key, libname):
    """ Test if Pyvmomi is present or not"""
    monkeypatch.setattr(vmware_module_utils, key, False)
    with pytest.raises(FailJsonException):
        vmware_module_utils.PyVmomi(fake_ansible_module)
    error_str = 'Failed to import the required Python library (%s)' % libname
    assert fake_ansible_module.fail_json.called_once()
    assert error_str in fake_ansible_module.fail_json.call_args[1]['msg']


@pytest.mark.skipif(sys.version_info < (2, 7), reason="requires python2.7 and greater")
@pytest.mark.parametrize("params, msg", test_data, ids=test_ids)
def test_required_params(request, params, msg, fake_ansible_module):
    """ Test if required params are correct or not"""
    fake_ansible_module.params = params
    with pytest.raises(FailJsonException):
        vmware_module_utils.connect_to_api(fake_ansible_module)
    assert fake_ansible_module.fail_json.called_once()
    assert msg in fake_ansible_module.fail_json.call_args[1]['msg']


def test_validate_certs(monkeypatch, fake_ansible_module):
    """ Test if SSL is required or not"""
    fake_ansible_module.params = test_data[3][0]

    monkeypatch.setattr(vmware_module_utils, 'ssl', None)
    with pytest.raises(FailJsonException):
        vmware_module_utils.PyVmomi(fake_ansible_module)
    msg = 'pyVim does not support changing verification mode with python < 2.7.9.' \
          ' Either update python or use validate_certs=false.'
    assert fake_ansible_module.fail_json.called_once()
    assert msg in fake_ansible_module.fail_json.call_args[1]['msg']


def test_vmdk_disk_path_split(monkeypatch, fake_ansible_module):
    """ Test vmdk_disk_path_split function"""
    fake_ansible_module.params = test_data[0][0]

    monkeypatch.setattr(vmware_module_utils, 'connect_to_api', fake_connect_to_api)
    pyv = vmware_module_utils.PyVmomi(fake_ansible_module)
    v = pyv.vmdk_disk_path_split('[ds1] VM_0001/VM0001_0.vmdk')
    assert v == ('ds1', 'VM_0001/VM0001_0.vmdk', 'VM0001_0.vmdk', 'VM_0001')


def test_vmdk_disk_path_split_negative(monkeypatch, fake_ansible_module):
    """ Test vmdk_disk_path_split function"""
    fake_ansible_module.params = test_data[0][0]

    monkeypatch.setattr(vmware_module_utils, 'connect_to_api', fake_connect_to_api)
    with pytest.raises(FailJsonException):
        pyv = vmware_module_utils.PyVmomi(fake_ansible_module)
        pyv.vmdk_disk_path_split('[ds1]')
    assert fake_ansible_module.fail_json.called_once()
    assert 'Bad path' in fake_ansible_module.fail_json.call_args[1]['msg']


@pytest.mark.skipif(sys.version_info < (2, 7), reason="requires python2.7 and greater")
def test_connect_to_api_validate_certs(monkeypatch, fake_ansible_module):
    monkeypatch.setattr(vmware_module_utils, 'connect', mock.Mock())

    def MockSSLContext(proto):
        ssl_context.proto = proto
        return ssl_context

    # New Python with SSLContext + validate_certs=True
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    monkeypatch.setattr(vmware_module_utils.ssl, 'SSLContext', MockSSLContext)
    fake_ansible_module.params['validate_certs'] = True
    vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto == ssl.PROTOCOL_SSLv23
    assert ssl_context.verify_mode == ssl.CERT_REQUIRED
    assert ssl_context.check_hostname is True
    vmware_module_utils.connect.SmartConnect.assert_called_once_with(
        host='esxi1',
        port=443,
        pwd='Esxi@123$%',
        user='Administrator@vsphere.local',
        sslContext=ssl_context)

    # New Python with SSLContext + validate_certs=False
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    monkeypatch.setattr(vmware_module_utils.ssl, 'SSLContext', MockSSLContext)
    fake_ansible_module.params['validate_certs'] = False
    vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto == ssl.PROTOCOL_SSLv23
    assert ssl_context.verify_mode == ssl.CERT_NONE
    assert ssl_context.check_hostname is False
    vmware_module_utils.connect.SmartConnect.assert_called_once_with(
        host='esxi1',
        port=443,
        pwd='Esxi@123$%',
        user='Administrator@vsphere.local',
        sslContext=ssl_context)

    # Old Python with no SSLContext + validate_certs=True
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    ssl_context.proto = None
    monkeypatch.delattr(vmware_module_utils.ssl, 'SSLContext')
    fake_ansible_module.params['validate_certs'] = True
    with pytest.raises(FailJsonException):
        vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto is None
    fake_ansible_module.fail_json.assert_called_once_with(msg=(
        'pyVim does not support changing verification mode with python '
        '< 2.7.9. Either update python or use validate_certs=false.'))
    assert not vmware_module_utils.connect.SmartConnect.called

    # Old Python with no SSLContext + validate_certs=False
    vmware_module_utils.connect.reset_mock()
    ssl_context = mock.Mock()
    ssl_context.proto = None
    monkeypatch.delattr(vmware_module_utils.ssl, 'SSLContext', raising=False)
    fake_ansible_module.params['validate_certs'] = False
    vmware_module_utils.connect_to_api(fake_ansible_module)
    assert ssl_context.proto is None
    vmware_module_utils.connect.SmartConnect.assert_called_once_with(
        host='esxi1',
        port=443,
        pwd='Esxi@123$%',
        user='Administrator@vsphere.local')