import subprocess
import unittest.mock

import pytest

from mnpw import nagios

OUTPUT_1 = """DISK OK - free space: / 3326 MB (56%);"""
OUTPUT_2 = (
    """ DISK OK - free space: / 3326 MB (56%); | /=2643MB;5948;5958;0;5968"""
)
OUTPUT_3 = """\
DISK OK - free space: / 3326 MB (56%); | /=2643MB;5948;5958;0;5968
/ 15272 MB (77%);
/boot 68 MB (69%);
/home 69357 MB (27%);
/var/log 819 MB (84%); | /boot=68MB;88;93;0;98
/home=69357MB;253404;253409;0;253414
/var/log=818MB;970;975;0;980
"""

# Lifted from the Alignak project:
# https://github.com/Alignak-monitoring/alignak/blob/fcc3d6499478ce67f4b91c694111f45cc8282535/tests/test_perfdata_parsing.py
PERF_DATA = [
    """ramused=90%;85;95;;""",
    """ramused=1009MB;;;0;1982 """,
    """memused=1550MB;2973;3964;0;5810""",
    """swapused=540MB;;;; """,
    """memused=90%""",
    """'Physical Memory Used'=12085620736Bytes; """,
    """Physical Memory Utilisation'=94%;80;90;""",
    """'C: used'=13.06452GB;22.28832;25.2601;0;29.71777 """,
    """'C: used %'=44%;75;85;0;100""",
    """time_offset-192.168.0.1=-7.22636468709e-05s;1;2;0;;""",
    """àéèï-192.168.0.1=-7.22636468709e-05s;1;2;0;;""",
    """Kernel=0;0;;0;2 Microcode=U;0;;0;1 Services=0;;0;0 Containers=0;;0;0 Sessions=0;0;;0""",  # noqa: E501
]


@pytest.mark.parametrize(
    "command,args,exit_code",
    [
        ("/bin/sh", ["-c", "exit 0"], nagios.NagiosCode.OK),
        ("/bin/sh", ["-c", "exit 1"], nagios.NagiosCode.WARNING),
        ("/bin/sh", ["-c", "exit 2"], nagios.NagiosCode.CRITICAL),
        ("/bin/sh", ["-c", "exit 3"], nagios.NagiosCode.UNKNOWN),
    ],
)
def test_check_exec(command, args, exit_code):
    """Test the execution of checks."""
    check = nagios.Check(command, args)
    check.run()
    assert check.ExitCode == exit_code


def test_check_command_not_found():
    """Test check with a nonexistent command."""
    check = nagios.Check("/abcdef", [])
    with pytest.raises(RuntimeError):
        check.run()


def test_check_timeout():
    """Test check with a timed out command."""
    check = nagios.Check("sleep", ["20"])
    with pytest.raises(RuntimeError):
        check.run(timeout=3)


def _mock_run(*args, **kwargs):
    """Mock subprocess' run."""
    return unittest.mock.patch.object(
        nagios.subprocess,
        "run",
        *args,
        **kwargs,
    )


@pytest.mark.parametrize(
    "exception",
    [
        FileNotFoundError,
        subprocess.TimeoutExpired(cmd="true", timeout=nagios.DEFAULT_TIMEOUT),
    ],
)
def test_check_exceptions(exception):
    """Test handling of subprocess' run exceptions.
    See
    https://docs.python.org/3/library/subprocess.html?highlight=popen#subprocess.SubprocessError
    """
    check = nagios.Check("true")
    with _mock_run(side_effect=exception), pytest.raises(RuntimeError):
        check.run()


@pytest.mark.parametrize("output", [OUTPUT_1, OUTPUT_2, OUTPUT_3])
def test_output_parsing(output):
    """Test parsing of plugin output."""
    check = nagios.Check("true")
    proc = subprocess.CompletedProcess(
        [
            "true",
        ],
        0,
        output,
        "",
    )
    with _mock_run(return_value=proc):
        check.run()
        assert isinstance(check.Output, str)
        assert len(check.Output.splitlines()) == 1


@pytest.mark.parametrize("line", PERF_DATA)
def test_data_perf(line):
    """Test parsing of perfdata."""
    pd = nagios.parse_perf_data(line)
    assert isinstance(pd.name, str)
    assert len(pd.name) > 0
    assert isinstance(pd.value, float)
    assert isinstance(pd.unit, (str, type(None)))
    assert isinstance(pd.warning, (float, type(None)))
    assert isinstance(pd.critical, (float, type(None)))
    assert isinstance(pd.min, (float, type(None)))
    assert isinstance(pd.max, (float, type(None)))