"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchDashboardDetails = fetchDashboardDetails;
exports.checkDashboardPermissions = checkDashboardPermissions;
// @ts-ignore
const javascript_wrapper_1 = __importDefault(require("@ytsaurus/javascript-wrapper"));
const yt = (0, javascript_wrapper_1.default)();
const check_permission_1 = require("../../../shared/utils/check-permission");
const cluster_queries_1 = require("../../components/cluster-queries");
const format_1 = require("../../../shared/utils/format");
const requestsSetup_1 = require("../../components/requestsSetup");
const prometheus_dashboards_1 = require("../../components/prometheus-dashboards");
const utils_1 = require("../../utils");
const utils_2 = require("../../../shared/prometheus/utils");
async function fetchDashboardDetails(req, { cluster, dashboardType, chartId, params, }) {
    let pos;
    if (chartId) {
        pos = (0, utils_2.parseXYFromPanelId)(typeof chartId === 'string' ? chartId : '');
        if (isNaN(pos.x) || isNaN(pos.y)) {
            throw new utils_1.ErrorWithCode(400, {
                message: 'Failed to parse x,y from chartId query parameter',
                attributes: { chartId },
            });
        }
    }
    const res = await req.ctx.call('Fetch dashboard details', async () => {
        const dashboard = await (0, prometheus_dashboards_1.getPreloadedPrometheusDashboard)({
            cluster,
            dashboardType,
        });
        let panel;
        if (chartId && pos) {
            const { x, y } = pos;
            panel = dashboard.panels.find((item) => {
                return item.gridPos.x === x && item.gridPos.y === y;
            });
            if (!panel || panel.type !== 'timeseries') {
                throw new utils_1.ErrorWithCode(400, {
                    message: `Failed to find details of a corresponding pannel`,
                    attributes: { cluster, dashboardType, x, y },
                });
            }
        }
        return { panel, templating: dashboard.templating };
    });
    const { permissions, list } = res.templating;
    const chartParams = list.reduce((acc, { name, default_for_ui }) => {
        const value = params[name] ?? default_for_ui;
        if (value !== undefined) {
            acc[name] = value;
        }
        return acc;
    }, {});
    await req.ctx.call('Check dashboard permissions', () => checkDashboardPermissions(req, {
        ytAuthCluster: cluster,
        permissions,
        chartParams,
    }));
    return Object.assign(res, { chartParams });
}
async function checkDashboardPermissions(req, { ytAuthCluster, permissions, chartParams, }) {
    const permissionsSubRequestsByCluster = new Map();
    permissions?.forEach(({ cluster: rawCluster = ytAuthCluster, path: rawPath, permission, ignorePaths }) => {
        // chartParams should contain only keys corresponding to items of `templating.list` of a dashboard description
        // from //sys/interface-monitoring/*, so it is safe to use it for RegExp
        // eslint-disable-next-line security/detect-non-literal-regexp
        const keyToRegex = (key) => new RegExp(`\\$${key}`, 'g');
        const path = (0, format_1.formatByParams)(rawPath, chartParams, { keyToRegex });
        if (ignorePaths?.some((item) => {
            const pathToIgnore = (0, format_1.formatByParams)(item, chartParams, { keyToRegex });
            return path === pathToIgnore;
        })) {
            return;
        }
        const cluster = (0, format_1.formatByParams)(rawCluster, chartParams, { keyToRegex });
        const subRequest = (0, check_permission_1.makeCheckPermissionBatchSubRequest)({
            path,
            user: '',
            permission,
        });
        const dst = permissionsSubRequestsByCluster.get(cluster);
        if (!dst) {
            permissionsSubRequestsByCluster.set(cluster, [subRequest]);
        }
        else {
            dst.push(subRequest);
        }
    });
    const permissionEntries = [...permissionsSubRequestsByCluster.entries()];
    const userWithSetup = await Promise.all(permissionEntries.map(([cluster]) => {
        const userYtApiSetup = (0, requestsSetup_1.getUserYTApiSetup)(cluster, req);
        return (0, cluster_queries_1.getXSRFToken)(req, userYtApiSetup).then(({ login, csrf_token }) => {
            return { cluster, user: login, userYtApiSetup, csrf_token };
        });
    }));
    const checkResults = await Promise.all(permissionEntries.map(([_cluster, requests], index) => {
        const { user, userYtApiSetup, csrf_token } = userWithSetup[index];
        const { setup } = userYtApiSetup;
        return yt.v3.executeBatch({
            setup: {
                ...setup,
                requestHeaders: {
                    ...setup.requestHeaders,
                    'X-Csrf-Token': csrf_token,
                },
            },
            parameters: {
                requests: requests.map((item) => {
                    Object.assign(item.parameters, { user });
                    return item;
                }),
            },
        });
    })).then((data) => {
        const error = {
            message: 'Check permissions error',
            inner_errors: [],
        };
        for (let i = 0; i < data.length; ++i) {
            const clusterResults = data[i];
            const [cluster, permissions] = permissionEntries[i];
            for (const item of clusterResults) {
                if (item.error) {
                    error.inner_errors?.push(item.error);
                }
                else if (item.output?.action !== 'allow') {
                    error.inner_errors?.push({
                        message: `Missing permission`,
                        attributes: { cluster, permissions },
                    });
                }
            }
        }
        if (error.inner_errors?.length) {
            throw new utils_1.ErrorWithCode(403, error);
        }
        return data;
    });
    return { checkResults, permissionEntries };
}
