#!/usr/bin/env python3

"""
Criticality: Normal
Checks that the streaming server (Wowza) is running correctly.
"""

from pathlib import Path
import re
import subprocess  # nosec: B404
import sys

from defusedxml.ElementTree import parse
from psutil import net_connections

sys.path.append(str(Path(__file__).parents[1].resolve()))

# pylint: disable=wrong-import-position
from utilities import logging as lg  # noqa: E402

LATEST_VERSION = "4.7.7"


def main():
    """Run all checks and exits with corresponding exit code."""

    print("Checking Wowza settings:")
    warnings = 0
    errors = 0

    # check if wowza is installed
    if not check_installed():
        exit(2)

    # check wowza version
    check_warn, check_err = check_version()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    # check wowza heap size
    check_warn, check_err = check_heap_size()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    # check that wowza is running
    check_warn, check_err = check_running()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    # check that wowza is listening
    check_warn, check_err = check_listening()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    if errors:
        exit(1)
    elif warnings:
        exit(3)

    exit(0)


def check_installed() -> bool:
    """Check that Wowza is installed.

    :return: Exit return codes
    :rtype: bool
    """

    cmd = "dpkg --get-selections 'wowza*'"
    out = subprocess.getoutput(cmd)
    state = out.split()[-1]

    if state != "install":
        lg.info("not installed, skip test")
        return False

    return True


def check_version() -> tuple:
    """Check the Wowza version installed.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    cmd = "dpkg --get-selections 'wowza*'"
    out = subprocess.getoutput(cmd)
    version = None

    for line in out.split("\n"):
        if line.split()[-1] == "install":
            if version:
                lg.error("many Wowza versions installed, keep only latest")
                errors += 1
            version = ".".join(re.findall(r"\d", line))

    if not version:
        lg.error("cannot find wWowza version")
        errors += 1

    if version != LATEST_VERSION:
        lg.warning("using outdated version: {}".format(version))
        warnings += 1
    else:
        lg.success("using recommended version: {}".format(LATEST_VERSION))

    return warnings, errors


def check_heap_size() -> tuple:
    """Check the heap size configured.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    cmd = "grep '<HeapSize>2000M</HeapSize>' /usr/local/WowzaStreamingEngine/conf/Tune.xml"
    check_heap, _ = subprocess.getstatusoutput(cmd)
    if check_heap != 0:
        lg.warning("not using recommended heap size")
        warnings += 1
    else:
        lg.success("using recommended heap size")

    return warnings, errors


def check_running() -> tuple:
    """Check that Wowza is running.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    cmd = "systemctl status WowzaStreamingEngine"
    out = subprocess.getoutput(cmd)
    if "Active: active (running)" not in out:
        lg.error("service not running")
        errors += 1
    else:
        lg.success("service running")

    return warnings, errors


def check_listening() -> tuple:
    """Check that Wowza is listening on configured port.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    # get port number in Wowza config
    conf = parse("/usr/local/WowzaStreamingEngine/conf/VHost.xml").getroot()
    port = conf.findall("VHost/HostPortList/HostPort/Port")[0].text

    # get listening ports
    listening = set(
        c.laddr[1] for c in net_connections(kind="inet") if c.status == "LISTEN"
    )

    # check that system is listening on this port
    if int(port) not in listening:
        lg.error("not listening on port {}".format(port))
        errors += 1
    else:
        lg.success("listening on port {}".format(port))

    return warnings, errors


if __name__ == "__main__":
    main()