--
-- (C) 2020-24 - ntop.org
--
--
-- This module implements an internet download bandwidth monitor.
--
local json = require("dkjson")
local ts_utils = require("ts_utils_core")
local format_utils = require("format_utils")

local do_trace = false
local collected_results = {}

-- #################################################################

-- The function called periodically to send the host probes.
-- hosts contains the list of hosts to probe, The table keys are
-- the hosts identifiers, whereas the table values contain host information
-- see (am_utils.key2host for the details on such format).
local function run_speedtest(hosts, granularity)
    local rsp
    local do_trace = false

    if (do_trace) then
        tprint("[run_speedtest] [" .. granularity .. "]\n")
    end

    rsp = ntop.speedtest()

    if (not rsp) then
        return
    end

    if do_trace then
        tprint(rsp)
    end

    rsp = json.decode(rsp)

    if (not rsp) then
        return
    end

    if not rsp["download.speed"] or not rsp["upload.speed"] then
        return
    end

    for key, host in pairs(hosts or {}) do
        -- NOTE: results are in Mbps -> for the timeseries convert into Bps
        if (rsp["download.speed"] ~= nil) then
            local isp = nil
            local download_mbit = rsp["download.speed"]

            if (type(rsp["client.isp"] == "string")) then
                isp = noHtml(rsp["client.isp"])
            end

            collected_results[key] = {
                value = download_mbit * 1000000 / 8,
                resolved_addr = isp
            }
        end

        if (rsp["upload.speed"]) then
            ts_utils.append("am_host:upload_" .. granularity, {
                ifid = getSystemInterfaceId(),
                speed = rsp["upload.speed"] * 1000000 / 8,
                host = host.host
            })
        end

        if (rsp["server.latency"]) then
            ts_utils.append("am_host:latency_" .. granularity, {
                ifid = getSystemInterfaceId(),
                latency = rsp["server.latency"],
                host = host.host
            })
        end
    end
end

-- #################################################################

-- The function responsible for collecting the results.
-- It must return a table containing a list of hosts along with their retrieved
-- measurement. The keys of the table are the host key. The values have the following format:
--  table
--	resolved_addr: (optional) the resolved IP address of the host
--	value: (optional) the measurement numeric value. If unspecified, the host is still considered unreachable.
local function collect_speedtest(granularity)
    if (trace) then
        print("[collect_speedtest] called\n")
    end

    return (collected_results)
end

-- #################################################################

-- This function checks whether the speedtest support has been compiled and available
local function check_speedtest_support()
    if (trace) then
        print("[check_speedtest_support] called\n")
    end

    return ntop.hasSpeedtestSupport()
end

-- #################################################################

return {
    -- Defines a list of measurements implemented by this script.
    -- The probing logic is implemented into the check() and collect_results().
    --
    -- Here is how the probing occurs:
    --	1. The check function is called with the list of hosts to probe. Ideally this
    --	   call should not block (e.g. should not wait for the results)
    --	2. The active_monitoring.lua code sleeps for some seconds
    --	3. The collect_results function is called. This should retrieve the results
    --       for the hosts checked in the check() function and return the results.
    --
    -- The alerts for non-responding hosts and the Active Monitoring timeseries are automatically
    -- generated by active_monitoring.lua . The timeseries are saved in the following schemas:
    -- "am_host:val_min", "am_host:val_5mins", "am_host:val_hour".
    measurements = {{
        -- The unique key for the measurement
        key = "speedtest",
        -- The localization string for this measurement
        i18n_label = "active_monitoring_stats.speedtest",
        -- The function called periodically to send the host probes
        check = run_speedtest,
        -- The function responsible for collecting the results
        collect_results = collect_speedtest,
        -- The granularities allowed for the probe. See supported_granularities in am_utils.lua
        granularities = { "hour" },
        -- The localization string for the measurement unit (e.g. "ms", "Mbits")
        i18n_unit = "field_units.mbits",
        -- The localization string for the Jitter unit (e.g. "ms", "Mbits")
        i18n_jitter_unit = nil,
        -- The localization string for the Active Monitoring timeseries menu entry
        i18n_am_ts_label = "active_monitoring_stats.download_speed",
        -- The localization string for the Active Monitoring metric in the chart
        i18n_am_ts_metric = "active_monitoring_stats.download_speed",
        -- The operator to use when comparing the measurement with the threshold, "gt" for ">" or "lt" for "<".
        operator = "lt",
        additional_timeseries = {{
            schema="am_host:upload",
            label=i18n("active_monitoring_stats.upload_speed"),
            metrics_labels = { i18n("active_monitoring_stats.upload_speed") },
            value_formatter = {"field_units.mbits"},
          }, {
            schema="am_host:latency",
            label=i18n("latency"),
            metrics_labels = { i18n("latency") },
            value_formatter = {"NtopUtils.fmillis"},
          }},
        -- If set, indicates a maximum threshold value
        max_threshold = 10000,
        -- If set, indicates the default threshold value
        default_threshold = nil,
        value_formatter = format_utils.bytesToBPS,
        -- The raw measurement value is multiplied by this factor before being written into the chart
        chart_scaling_value = 1000000,
        -- A list of additional notes (localization strings) to show into the timeseries charts
        i18n_chart_notes = {},
        -- If set, the user cannot change the host
        force_host = "speedtest.net",
        -- An alternative localization string for the unrachable alert message
        unreachable_alert_i18n = "alert_messages.speedtest_failed"
    }},
    setup = check_speedtest_support
}
