local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
require "lua_utils"

local ts_rest_utils = {}

function ts_rest_utils.get_timeseries(http_context)
    local graph_common = require "graph_common"
    local graph_utils = require "graph_utils"
    local ts_utils = require("ts_utils")
    local ts_common = require("ts_common")

    local ts_schema = http_context.ts_schema
    local tstart = http_context.epoch_begin
    local tend = http_context.epoch_end
    local compare_backward = http_context.ts_compare
    local tags = http_context.tags
    local extended_times = http_context.extended
    local ts_aggregation = http_context.ts_aggregation
    local no_fill = tonumber(http_context.no_fill)

    tstart = tonumber(tstart) or (os.time() - 3600)
    tend = tonumber(tend) or os.time()
    if http_context.tskey then
        -- This can contain a MAC address for local broadcast domain hosts
        local tskey = http_context.tskey

        -- Setting host_ip (check that the provided IP matches the provided
        -- mac address as safety check and to avoid security issues)
        if (ts_schema == "top:snmp_if:traffic") or (ts_schema == "top:flowdev_port:traffic") then
            -- NOTE: the host here is not required, if added return an empty serie
            tskey = 0
            tags.host = nil
        end

        if tags.host then
            local host = hostkey2hostinfo(tags.host)

            if not isEmptyString(host["host"]) then
                local serialize_by_mac = ntop.getPref(string.format(
                    "ntopng.prefs.ifid_" .. tags.ifid .. ".serialize_local_broadcast_hosts_as_macs")) == "1"
                local host_info = interface.getHostInfo(host["host"], host["vlan"])
                -- local mac_info = split(tskey, "_")
                -- if (host_info) and (host_info.mac == mac_info[1]) then
                --    tags.host_ip = tags.host;
                if (host_info ~= nil) then
                    local mac_info = split(tskey, "_")

                    if (host_info.name ~= nil) then
                        -- Add the symbolic host name (if present)
                        tags.label = host_info.name
                    end

                    if (host_info.mac == mac_info[1]) then
                        tags.host_ip = tags.host;
                    end

                    if (host_info.mac) then
                        tags.mac = host_info.mac;
                    end
                end
            end
        end

        if tskey ~= 0 then
            tags.host = tskey
        end
    end

    local options = {
        max_num_points = tonumber(http_context.limit) or 60,
        initial_point = toboolean(http_context.initial_point),
        with_series = true,
        target_aggregation = ts_aggregation or "raw"
    }

    if (no_fill == 1) then
        options.fill_value = 0 / 0 -- NaN
    end

    if tags.ifid then
        interface.select(tags.ifid)
    end

    if ((ts_schema == "top:flow_check:duration") or (ts_schema == "top:elem_check:duration") or
        (ts_schema == "custom:flow_check:total_stats") or (ts_schema == "custom:elem_check:total_stats")) then
        -- NOTE: Temporary fix for top checks page
        tags.check = nil
    end

    local function performQuery(tstart, tend, keep_total, additional_options)
        local res
        additional_options = additional_options or {}
        local options = table.merge(options, additional_options)
        options.keep_nan = true
        if starts(ts_schema, "top:") then
            local ts_schema = split(ts_schema, "top:")[2]

            res = ts_utils.queryTopk(ts_schema, tags, tstart, tend, options)
        else
            res = ts_utils.query(ts_schema, tags, tstart, tend, options)

            if (not keep_total) and (res) and (res.additional_series) then
                -- no need for total serie in normal queries
                res.additional_series.total = nil
            end
        end

        return res
    end

    local res

    if (ntop.getPref("ntopng.prefs.ndpi_flows_rrd_creation") == "1") then
        if (ts_schema == "host:ndpi") then
            ts_schema = "custom:host_ndpi_and_flows"
        elseif (ts_schema == "iface:ndpi") then
            ts_schema = "custom:iface_ndpi_and_flows"
        end
    end

    if starts(ts_schema, "custom:") and graph_utils.performCustomQuery then
        res = graph_utils.performCustomQuery(ts_schema, tags, tstart, tend, options)
        compare_backward = nil
    else
        -- if Mac address ts is requested, check if the serialize by mac is enabled and if no data is found, use the host timeseries. 
        -- if (table.len(res) == 0) or (res.statistics) and (res.statistics.total == 0) then
        local serialize_by_mac = ntop.getPref(string.format("ntopng.prefs.ifid_" .. tags.ifid ..
                                                                ".serialize_local_broadcast_hosts_as_macs")) == "1"
        local tmp = split(ts_schema, ":")

        if (serialize_by_mac) and (tags.mac) then
            ts_schema = "host:" .. tmp[2]
            tags.host = tags.mac .. "_v4"
        end

        res = performQuery(tstart, tend) or {}
        -- end
    end

    if res == nil then
        res = {}

        if (ts_utils.getLastError() ~= nil) then
            res["tsLastError"] = ts_utils.getLastError()
            res["error"] = ts_utils.getLastErrorMessage()
            rest_utils.answer(rest_utils.consts.err.internal_error, res)
        else
            rest_utils.answer(rest_utils.consts.success.ok, res)
        end

        return
    end

    -- Add metadata
    res.schema = ts_schema
    res.query = tags
    res.max_points = options.max_num_points

    if not isEmptyString(compare_backward) and compare_backward ~= "1Y" and (res.step ~= nil) then
        local backward_sec = graph_common.getZoomDuration(compare_backward)
        local tstart_cmp = res.start - backward_sec
        local tend_cmp = tstart_cmp + res.step * (res.count - 1)

        -- Try to use the same aggregation as the original query
        local res_cmp = performQuery(tstart_cmp, tend_cmp, true, {
            target_aggregation = res.source_aggregation
        }) or {}
        local total_cmp_serie = nil

        if res_cmp and res_cmp.additional_series and res_cmp.additional_series.total and (res_cmp.step) and res_cmp.step >=
            res.step then
            total_cmp_serie = res_cmp.additional_series.total

            if res_cmp.step > res.step then
                -- The steps may not still correspond if the past query overlaps a retention policy
                -- bound (it will have less points, but with an higher step), upscale to solve this
                total_cmp_serie = ts_common.upsampleSerie(total_cmp_serie, res.count)
            end
        end

        if total_cmp_serie then
            res.additional_series = res.additional_series or {}
            res.additional_series[compare_backward .. " " .. i18n("details.ago")] = total_cmp_serie
        end
    end

    -- TODO make a script parameter?
    local extend_labels = true

    if extend_labels and graph_utils.extendLabels then
        graph_utils.extendLabels(res)
    end

    if (res.query) and (res.query.if_index) then
        local snmp_utils = require "snmp_utils"
        local snmp_cached_dev = require "snmp_cached_dev"
        local cached_device = snmp_cached_dev:create(tags.device)
        res.query.label = shortenString(snmp_utils.get_snmp_interface_label(
            cached_device["interfaces"][res.query.if_index]), 64)
    end

    -- Add layout information
    local layout = graph_utils.get_timeseries_layout(ts_schema)
    local filtered_serie = {}

    for _, serie in pairs(res.series or {}) do
        if not serie.type then
            if layout[serie.label] then
                serie.type = layout[serie.label]
            end
        end

        filtered_serie[#filtered_serie + 1] = serie
    end

    res.series = filtered_serie

    if extended_times then
        if res.series and res.step then
            for k, serie in pairs(res.series) do
                serie.data = ts_common.serieWithTimestamp(serie.data, tstart, res.step)
            end
        end
        if res.additional_series and res.step then
            for k, serie in pairs(res.additional_series) do
                res.additional_series[k] = ts_common.serieWithTimestamp(serie, tstart, res.step)
            end
        end
    end

    return res
end -- end get_timeseries

return ts_rest_utils
