/*
 * Copyright (C) 2017 Vsevolod Stakhov <vsevolod@highsecure.ru>
 * Copyright (C) 2017 Alexander Moisseev
 */

define(["app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", "tabulator"],
    (common, tabUtils, D3Evolution, D3Pie, d3, Tabulator) => {
        "use strict";

        const rrd_pie_config = {
            cornerRadius: 2,
            size: {
                canvasWidth: 400,
                canvasHeight: 180,
                pieInnerRadius: "50%",
                pieOuterRadius: "80%"
            },
            labels: {
                outer: {
                    format: "none"
                },
                inner: {
                    hideWhenLessThanPercentage: 8,
                    offset: 0
                },
            },
            padAngle: 0.02,
            pieCenterOffset: {
                x: -120,
                y: 10,
            },
            total: {
                enabled: true
            },
        };

        const ui = {};
        let prevUnit = "msg/s";

        const rrdUnitColumns = ["min", "avg", "max", "last"];
        function rrdColumnTitle(field, unit) {
            const titles = {min: "Minimum", avg: "Average", max: "Maximum", last: "Last"};
            const label = titles[field];
            return label ? label + ", <span class=\"unit\">" + unit + "</span>" : "";
        }

        ui.draw = function (graphs, neighbours, checked_server, type) {
            const graph_options = {
                title: "Rspamd throughput",
                width: 1060,
                height: 370,
                yAxisLabel: "Message rate, msg/s",

                legend: {
                    space: 140,
                    entries: common.chartLegend
                }
            };

            function initGraph() {
                const graph = new D3Evolution("graph", {
                    ...graph_options,
                    yScale: common.getSelector("selYScale"),
                    type: common.getSelector("selType"),
                    interpolate: common.getSelector("selInterpolate"),
                    convert: common.getSelector("selConvert"),
                });
                document.getElementById("selYScale").addEventListener("change", (e) => {
                    graph.yScale(e.currentTarget.value);
                });
                document.getElementById("selConvert").addEventListener("change", (e) => {
                    graph.convert(e.currentTarget.value);
                });
                document.getElementById("selType").addEventListener("change", (e) => {
                    graph.type(e.currentTarget.value);
                });
                document.getElementById("selInterpolate").addEventListener("change", (e) => {
                    graph.interpolate(e.currentTarget.value);
                });

                return graph;
            }

            function getRrdSummary(json, scaleFactor) {
                const xExtents = d3.extent(d3.merge(json), (d) => d.x);
                const timeInterval = xExtents[1] - xExtents[0];

                let total = 0;
                const rows = json.map((curr, i) => {
                    // Time intervals that don't have data are excluded from average calculation as d3.mean()ignores nulls
                    const avg = d3.mean(curr, (d) => d.y);
                    // To find an integral on the whole time interval we need to convert nulls to zeroes
                    // eslint-disable-next-line no-bitwise
                    const value = d3.mean(curr, (d) => Number(d.y)) * timeInterval / scaleFactor ^ 0;
                    const yExtents = d3.extent(curr, (d) => d.y);

                    total += value;
                    return {
                        label: graph_options.legend.entries[i].label,
                        value: value,
                        min: Number((yExtents[0] ?? 0).toFixed(6)),
                        avg: Number((avg ?? 0).toFixed(6)),
                        max: Number((yExtents[1] ?? 0).toFixed(6)),
                        last: Number((curr[curr.length - 1]?.y ?? 0).toFixed(6)),
                        color: graph_options.legend.entries[i].color,
                    };
                }, []);

                return {
                    rows: rows,
                    total: total
                };
            }

            function initSummaryTable(rows, unit) {
                common.tables.rrd_summary = new Tabulator("#rrd-table", {
                    layout: "fitColumns",
                    selectable: false,
                    index: "label",
                    data: rows,
                    columns: [
                        {title: "Action", field: "label", minWidth: 90},
                        {title: "Messages", field: "value", minWidth: 100},
                        {title: rrdColumnTitle("min", unit), field: "min", minWidth: 149},
                        {title: rrdColumnTitle("avg", unit), field: "avg", minWidth: 143},
                        {title: rrdColumnTitle("max", unit), field: "max", minWidth: 151},
                        {title: rrdColumnTitle("last", unit), field: "last", minWidth: 121},
                    ],
                    rowFormatter: function (row) {
                        const c = row.getData().color;
                        if (c) row.getElement().style.setProperty("--rrd-row-color", c);
                    },
                });
                tabUtils.patchScrollIntoViewOnce();
                tabUtils.stripTableholderTabindex("rrd_summary");
            }

            function drawRrdTable(rows, unit) {
                if (Object.prototype.hasOwnProperty.call(common.tables, "rrd_summary")) {
                    common.tables.rrd_summary.updateData(rows);
                    if (unit !== prevUnit) {
                        rrdUnitColumns.forEach((field) => {
                            common.tables.rrd_summary.updateColumnDefinition(field, {
                                title: rrdColumnTitle(field, unit),
                            });
                        });
                    }
                } else {
                    initSummaryTable(rows, unit);
                }
            }

            function updateWidgets(data) {
                let rrd_summary = {rows: []};
                let unit = "msg/s";

                if (data) {
                    // Autoranging
                    let scaleFactor = 1;
                    const yMax = d3.max(d3.merge(data), (d) => d.y);
                    if (yMax < 1) {
                        scaleFactor = 60;
                        unit = "msg/min";
                        data.forEach((s) => {
                            s.forEach((d) => {
                                if (d.y !== null) { d.y *= scaleFactor; }
                            });
                        });
                    }

                    rrd_summary = getRrdSummary(data, scaleFactor);
                }

                if (!graphs.rrd_pie) graphs.rrd_pie = new D3Pie("rrd-pie", rrd_pie_config);
                graphs.rrd_pie.data(rrd_summary.rows);

                graphs.graph.data(data);
                if (unit !== prevUnit) {
                    graphs.graph.yAxisLabel("Message rate, " + unit);
                    document.querySelectorAll(".unit").forEach((el) => {
                        el.textContent = unit;
                    });
                    prevUnit = unit;
                }
                drawRrdTable(rrd_summary.rows, unit);
                document.getElementById("rrd-total-value").innerHTML = rrd_summary.total;
            }

            if (!graphs.graph) {
                graphs.graph = initGraph();
            }


            common.query("graph", {
                success: function (req_data) {
                    let data = null;
                    const neighbours_data = req_data
                        .filter((d) => d.status) // filter out unavailable neighbours
                        .map((d) => d.data);

                    if (neighbours_data.length === 1) {
                        [data] = neighbours_data;
                    } else {
                        let time_match = true;
                        neighbours_data.reduce((res, curr, _, arr) => {
                            if ((curr[0][0].x !== res[0][0].x) ||
                            (curr[0][curr[0].length - 1].x !== res[0][res[0].length - 1].x)) {
                                time_match = false;
                                common.logError({
                                    server: "Multi-server",
                                    endpoint: "graph",
                                    message: "Neighbours time extents do not match. " +
                                        "Check if time is synchronized on all servers.",
                                    errorType: "data_inconsistency"
                                });
                                arr.splice(1); // Break out of .reduce() by mutating the source array
                            }
                            return curr;
                        });

                        if (time_match) {
                            data = neighbours_data.reduce((res, curr) => curr.map((action, j) => action.map((d, i) => ({
                                x: d.x,
                                y: (res[j][i].y === null) ? d.y : res[j][i].y + d.y
                            }))));
                        }
                    }
                    updateWidgets(data);
                },
                complete: function () {
                    const refreshBtn = document.getElementById("refresh");
                    refreshBtn.disabled = false;
                    refreshBtn.classList.remove("disabled");
                },
                errorMessage: "Cannot receive throughput data",
                errorOnceId: "alerted_graph_",
                data: {type: type}
            });
        };


        // Handling mouse events on overlapping elements
        function setZIndex(selector, value) {
            document.querySelectorAll(selector).forEach((el) => {
                el.style.zIndex = value;
            });
        }
        document.getElementById("rrd-pie").addEventListener("mouseover", () => {
            setZIndex("#rrd-pie, #rrd-pie-tooltip", "200");
            setZIndex("#rrd-table_toggle", "300");
        });
        document.getElementById("rrd-table_toggle").addEventListener("mouseover", () => {
            setZIndex("#rrd-pie, #rrd-pie-tooltip, #rrd-table_toggle", "0");
        });

        return ui;
    });
