import os
import pytest
import hashlib
import time
import six

from faker import Faker
from saltcontainers.factories import ContainerFactory

from tests.common import GRAINS_EXPECTATIONS
import json

def pytest_generate_tests(metafunc):
    '''
    Call listed functions with the grain params.
    '''

    functions = [
        'test_ssh_grain_os',
        'test_ssh_grain_oscodename',
        'test_ssh_grain_os_family',
        'test_ssh_grain_osfullname',
        'test_ssh_grain_osrelease',
        'test_ssh_grain_osrelease_info',
    ]

    expectations = GRAINS_EXPECTATIONS

    # TODO: Replace this construction with just reading a current version
    version = set(set(metafunc.config.getini('TAGS'))).intersection(set(expectations)).pop()
    if metafunc.function.__name__ in functions and version:
        metafunc.parametrize('expected', [expectations[version]], ids=lambda it: version)
    if 'python' in metafunc.fixturenames:
        tags = set(metafunc.config.getini('TAGS'))
        if 'sles15' in tags or 'sles15sp1' in tags or 'sles15sp2' in tags or 'rhel8' in tags or 'opensuse' in tags:
            metafunc.parametrize("python", ["python3"])
        else:
            metafunc.parametrize("python", ["python"])


def test_ssh_grain_os(master, container, expected):
    grain = 'os'
    assert master.salt_ssh(
        container, "grains.get {}".format(grain)) == expected[grain]


def test_ssh_grain_oscodename(master, container, expected):
    grain = 'oscodename'
    assert master.salt_ssh(
        container, "grains.get {}".format(grain)) == expected[grain]


def test_ssh_grain_os_family(master, container, expected):
    grain = 'os_family'
    assert master.salt_ssh(
        container, "grains.get {}".format(grain)) == expected[grain]


def test_ssh_grain_osfullname(master, container, expected):
    grain = 'osfullname'
    assert master.salt_ssh(
        container, "grains.get {}".format(grain)) == expected[grain]


def test_ssh_grain_osrelease(master, container, expected):
    grain = 'osrelease'
    assert master.salt_ssh(
        container, "grains.get {}".format(grain)) == expected[grain]


def test_ssh_grain_osrelease_info(master, container, expected):
    grain = 'osrelease_info'
    assert master.salt_ssh(
        container, "grains.get {}".format(grain)) == expected[grain]


def test_ssh_ping(master, container):
    '''
    Test test.ping working.
    '''
    assert master.salt_ssh(container, "test.ping") is True


def test_ssh_cmdrun(master, container):
    '''
    Test grains over Salt SSH
    '''
    assert master.salt_ssh(container, "cmd.run 'uname'") == 'Linux'


@pytest.mark.skiptags('ubuntu')
def test_ssh_pkg_info(master, container):
    '''
    Test pkg.info_instaled on RHEL series
    '''
    assert master.salt_ssh(
        container,
        "pkg.info_installed test-package").get('test-package', {}).get('install_date')


@pytest.mark.xfailtags('rhel')
def test_ssh_sysdoc(master, container):
    '''
    Test sys.doc remote execution.
    '''
    assert 'cmd.run' in master.salt_ssh(container, "sys.doc")


@pytest.mark.skiptags('ubuntu')
def test_ssh_pkg_install(master, container):
    '''
    Test pkg.install
    '''
    master.salt_ssh(container, "cmd.run 'rpm -e test-package'")
    out = master.salt_ssh(container, "pkg.install test-package")
    assert out.get('test-package', {}).get('new')
    assert not out.get('test-package', {}).get('old')


@pytest.mark.tags('rhel')
def test_ssh_pkg_remove_rhel(master, container):
    '''
    Test pkg.remove on RHEL
    '''
    master.salt_ssh(container, "cmd.run 'yum install test-package -y'")
    out = master.salt_ssh(container, "pkg.remove test-package")
    assert out.get('test-package', {}).get('old')
    assert not out.get('test-package', {}).get('new')


@pytest.mark.tags('sles', 'opensuse')
def test_ssh_pkg_remove_sles(master, container):
    '''
    Test pkg.remove on SLES
    '''
    master.salt_ssh(
        container, "cmd.run 'zypper --non-interactive in test-package'")
    out = master.salt_ssh(container, "pkg.remove test-package")
    assert out.get('test-package', {}).get('old')
    assert not out.get('test-package', {}).get('new')


def test_master_tops_support(master, container):
    '''
    Test https://github.com/saltstack/salt/pull/38021
    '''
    assert 'custom_top' in master.salt_ssh(container, "state.show_top").get('base')


@pytest.mark.skiptags('ubuntu')
def test_ssh_port_forwarding(master, container, python):
    '''
    Test SSH port forwarding feature.
    PR: https://github.com/saltstack/salt/pull/38021
    '''
    if six.PY3:
        msg = hashlib.sha256(str(time.time()).encode()).hexdigest()
    else:
        msg = hashlib.sha256(str(time.time())).hexdigest()
    nc = "/salt-toaster/tests/scripts/netsend.sh"
    of = "/tmp/socket-8888.txt"
    loc_port = 8888
    rem_port = 9999

    master['container'].run("{py} /salt-toaster/tests/scripts/socket_server.py {lp} {of}".format(py=python, lp=loc_port, of=of))
    params = "--remote-port-forwards={rp}:127.0.0.1:{lp} cmd.run '{nc} {msg} {rp}'".format(
        nc=nc, msg=msg, lp=loc_port, rp=rem_port)
    master.salt_ssh(container, params)

    assert str(master['container'].run("cat {}".format(of)).strip().decode()) == msg


@pytest.fixture(scope="module")
def sshdcontainer(request, salt_root):
    fake = Faker()
    obj = ContainerFactory(
        config__name='ssdcontainer_{0}_{1}_{2}'.format(fake.word(), fake.word(), os.environ.get('ST_JOB_ID', '')),  # pylint: disable=no-member
        config__image=request.config.getini('MINION_IMAGE') or request.config.getini('IMAGE'),
        config__salt_config=None,
        ssh_config={'user': 'root', 'password': 'admin123', 'port': 22})
    obj.run('zypper --non-interactive rm salt')  # Remove salt from the image!!

    request.addfinalizer(obj.remove)
    return obj


def test_ssh_option(master, sshdcontainer):
    '''
    Test test.ping working.
    '''

    master["container"]["config"]["salt_config"]["roster"].append(sshdcontainer)
    master.update_roster()

    master['container'].run("zypper --non-interactive in netcat-openbsd")
    SSH = (
        "salt-ssh -l quiet -i --out json --key-deploy --passwd admin123 "
        "--ssh-option='ProxyCommand=\"nc {0} 2222\"' {1} network.ip_addrs"
    ).format(sshdcontainer['ip'], sshdcontainer['config']['name'])
    return json.loads(str(master['container'].run(SSH).decode())).get('target') == sshdcontainer['ip']