#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

import copy


def _transform_check_http(params):
    if isinstance(params, dict):
        return params
    name, mode = copy.deepcopy(params)
    mode_name = "cert" if "cert_days" in mode else "url"

    transformed = {"name": name, "mode": (mode_name, mode)}
    # The proxy option has ben isolated in version 1.6.0i1
    proxy_address = mode.get("proxy")
    if proxy_address:
        proxy = transformed.setdefault("proxy", {"address": proxy_address})
        # ":" outside a IPv6 address indicates port
        if ":" in proxy_address.split("]")[-1]:
            addr, port = proxy_address.rsplit(":", 1)
            try:
                proxy["port"] = int(port)
                proxy["address"] = addr
            except ValueError:
                pass  # leave address as it is
        if "proxy_auth" in mode:
            proxy["auth"] = mode["proxy_auth"]

    # The host options have ben isolated in version 1.6.0i1
    host_settings = transformed.setdefault("host", {})
    # URL mode:
    if "virthost" in mode:
        virthost, omit_ip = mode.pop("virthost")
        host_settings["virthost"] = virthost
        if omit_ip or proxy_address:
            host_settings["address"] = virthost
    # CERT mode:
    if "cert_host" in mode:
        host_settings["address"] = mode.pop("cert_host")
    # both modes:
    for key in ("port", "address_family"):
        if key in mode:
            host_settings[key] = mode.pop(key)

    mode.pop("sni", None)

    return transformed


def _get_host(params):
    # Use the address family of the monitored host by default
    host = params.get("host", {})

    family = host.get("address_family")
    if family is None:
        family = "ipv6" if is_ipv6_primary(host_name()) else "ipv4"

    address = host.get("address")
    if address is None:
        address = "$_HOSTADDRESS_%s$" % family[-1]

    HostSettings = collections.namedtuple(  # pylint: disable=collections-namedtuple-call
        "HostSettings", ("address", "port", "family", "virtual")
    )
    return HostSettings(address, host.get("port"), family, host.get("virthost"))


def _get_proxy(params):
    """return proxys (address, port, auth) or None"""
    proxy = params.get("proxy")
    if not proxy:
        return None

    auth = proxy.get("auth")
    if auth:
        auth = passwordstore_get_cmdline("%s:%%s" % auth[0], auth[1])

    ProxySettings = collections.namedtuple(  # pylint: disable=collections-namedtuple-call
        "ProxySettings", ("address", "port", "auth")
    )
    return ProxySettings(proxy.get("address"), proxy.get("port"), auth)


def _certificate_args(host, proxy, settings):
    args = []

    if "cert_days" in settings:
        # legacy behavior
        if isinstance(settings["cert_days"], int):
            args += ["-C", settings["cert_days"]]
        else:
            warn, crit = settings["cert_days"]
            args += ["-C", "%d,%d" % (warn, crit)]

    if proxy:
        args += ["--ssl", "-j", "CONNECT"]

    return args


def _url_args(host, _proxy, settings):
    args = []

    if "uri" in settings:
        args += ["-u", settings["uri"]]

    ssl = settings.get("ssl")
    if ssl in [True, "auto"]:
        args.append("--ssl")
    elif ssl:
        args.append("--ssl=%s" % ssl)

    if "response_time" in settings:
        warn, crit = settings["response_time"]
        args += ["-w", "%f" % (warn / 1000.0), "-c", "%f" % (crit / 1000.0)]

    if "timeout" in settings:
        args += ["-t", settings["timeout"]]

    if "user_agent" in settings:
        args += ["-A", settings["user_agent"]]

    for header in settings.get("add_headers", []):
        args += ["-k", header]

    if "auth" in settings:
        username, password = settings["auth"]
        args += ["-a", passwordstore_get_cmdline("%s:%%s" % username, password)]

    if "onredirect" in settings:
        args.append("--onredirect=%s" % settings["onredirect"])

    if "expect_response" in settings:
        args += ["-e", ",".join(settings["expect_response"])]

    if "expect_string" in settings:
        args += ["-s", settings["expect_string"]]

    if "expect_response_header" in settings:
        args += ["-d", settings["expect_response_header"]]

    if "expect_regex" in settings:
        if len(settings["expect_regex"]) >= 4 and settings["expect_regex"][3]:
            args.append("-l")
        if settings["expect_regex"][1]:
            args.append("-R")
        else:
            args.append("-r")
        args += [settings["expect_regex"][0]]
        if settings["expect_regex"][2]:
            args.append("--invert-regex")

    if settings.get("extended_perfdata"):
        args.append("--extended-perfdata")

    if "post_data" in settings:
        data, content_type = settings["post_data"]
        args += ["-P", data, "-T", content_type]

    if "method" in settings:
        args += ["-j", settings["method"]]

    if settings.get("no_body"):
        args.append("--no-body")

    if "page_size" in settings:
        args += ["-m", "%d:%d" % settings["page_size"]]

    if "max_age" in settings:
        args += ["-M", settings["max_age"]]

    # FIXME: This option is deprecated. According to the monitoring-plugins
    # the "urlize" plugin should be used.
    if settings.get("urlize"):
        args.append("-L")

    return args


def _server_address(host, proxy):
    if proxy:
        return proxy.address
    return host.address


def _host_name(host, proxy, params):
    if proxy and host.port:
        return "%s:%s" % (host.address, host.port)
    if proxy:
        return host.address
    if host.virtual:
        return host.virtual
    if params["mode"][0] == "cert":
        return host.address
    # Don't return the address in this case, because check_http would
    # automatically set the HTTP Host header and use HTTP/1.1 instead of
    # HTTP/1.0. This can lead to request timeouts on hosts which are
    # not compliant with HTTP/1.1.
    return None


def _common_args(host, proxy, params):
    args = []

    if host.family == "ipv6":
        args.append("-6")
    if not params.get("disable_sni"):
        args.append("--sni")
    if proxy and proxy.auth:
        args += ["-b", proxy.auth]

    specify_port = proxy.port if proxy else host.port
    if specify_port:
        args += ["-p", "%s" % specify_port]

    # last two arguments correspond to -I/-H, respectively
    args += [_server_address(host, proxy)]
    host_name = _host_name(host, proxy, params)
    if host_name:
        args += [host_name]

    return args


def check_http_arguments(params):
    params = _transform_check_http(params)

    mode_name, settings = params["mode"]

    host = _get_host(params)
    proxy = _get_proxy(params)

    if mode_name == "cert":
        args = _certificate_args(host, proxy, settings)
    else:
        args = _url_args(host, proxy, settings)

    return args + _common_args(host, proxy, params)


def check_http_description(params):
    params = _transform_check_http(params)

    description = params["name"]
    if description.startswith("^"):
        return description[1:]

    mode_name, settings = params["mode"]
    # here we have to cover connection and certificate checks
    if settings.get("ssl") or mode_name == "cert":
        return "HTTPS %s" % description
    return "HTTP %s" % description


active_check_info["http"] = {
    "command_line": "check_http $ARG1$",
    "argument_function": check_http_arguments,
    "service_description": check_http_description,
    "has_perfdata": True,
}
