"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPreloadedClusterParams = getPreloadedClusterParams;
exports.getPreloadedClusterUiConfig = getPreloadedClusterUiConfig;
const crypto_1 = __importDefault(require("crypto"));
const os = __importStar(require("os"));
// @ts-ignore
const javascript_wrapper_1 = __importDefault(require("@ytsaurus/javascript-wrapper"));
const constants_1 = require("../../shared/constants");
const snake_to_camel_1 = require("../../shared/utils/snake-to-camel");
const ServerFactory_1 = require("../ServerFactory");
const auto_updated_cache_1 = require("../utils/auto-updated-cache");
const requestsSetup_1 = require("./requestsSetup");
const yt = (0, javascript_wrapper_1.default)();
class BatchSubrequestError extends Error {
    constructor(response) {
        super();
        this.response = response;
    }
}
function fetchClusterParams(cluster, { ctx }) {
    const app = (0, ServerFactory_1.getApp)();
    return (ctx ?? app.nodekit.ctx).call('fetchClusterParams', async (cx) => {
        if (!cx.get('requestId')) {
            const requestId = crypto_1.default.randomUUID();
            cx.set('requestId', requestId);
            cx.setTag('request_id', requestId);
        }
        const timeStart = Date.now();
        const { setup: configSetup } = (0, requestsSetup_1.getRobotYTApiSetup)(cluster);
        const xYTCorrelationId = `${cx.get('requestId')}.fetchClusterParams`;
        const mlSuffix = '.masterList';
        function sendStats(responseStatus, suffix = '') {
            const timestamp = Date.now();
            cx.log('fetchClusterParams' + suffix, {
                xYTCorrelationId: xYTCorrelationId + suffix,
                responseStatus,
            });
            cx.stats?.('ytRequests', {
                responseStatus,
                headerContentLength: 0,
                timestamp,
                host: os.hostname(),
                service: cluster,
                requestTime: timestamp - timeStart,
                requestId: cx.get('requestId') + suffix,
                action: 'fetchClusterParams' + suffix,
            });
        }
        const mastersList = await yt.v3
            .executeBatch({
            setup: {
                ...configSetup,
                requestHeaders: {
                    ...cx.getMetadata(),
                    'X-YT-Correlation-Id': xYTCorrelationId + mlSuffix,
                },
                transformResponse({ parsedData, rawResponse }) {
                    return {
                        data: parsedData,
                        status: rawResponse?.status,
                        headers: rawResponse?.headers,
                    };
                },
                transformError({ parsedData, rawError }) {
                    throw {
                        data: parsedData,
                        status: rawError?.response?.status,
                        headers: rawError?.response?.headers,
                    };
                },
            },
            parameters: {
                requests: [
                    {
                        command: 'list',
                        parameters: {
                            path: '//sys/primary_masters',
                            ...constants_1.USE_SUPRESS_SYNC,
                        },
                    },
                ],
            },
        })
            .then((response) => {
            cx.log('List of masters is fetched', {
                cluster,
                'x-yt-proxy': response.headers['x-yt-proxy'],
                'x-yt-request-id': response.headers['x-yt-request-id'],
                'x-yt-trace-id': response.headers['x-yt-trace-id'],
            });
            sendStats(response.status, mlSuffix);
            return response.data[0];
        })
            .catch((error) => {
            const headers = error?.headers || {};
            cx.logError('List of masters fetch failed', {
                cluster,
                'x-yt-proxy': headers['x-yt-proxy'],
                'x-yt-request-id': headers['x-yt-request-id'],
                'x-yt-trace-id': headers['x-yt-trace-id'],
            });
            sendStats(error.status, mlSuffix);
            return Promise.reject(error.data);
        });
        const [mediumList, schedulerVersion, uiConfig, uiDevConfig, masterVersion] = await yt.v3
            .executeBatch({
            setup: {
                ...configSetup,
                requestHeaders: { ...cx.getMetadata(), 'X-YT-Correlation-Id': xYTCorrelationId },
                transformResponse({ parsedData, rawResponse }) {
                    return {
                        data: parsedData,
                        status: rawResponse?.status,
                        headers: rawResponse?.headers,
                    };
                },
                transformError({ parsedData, rawError }) {
                    throw {
                        data: parsedData,
                        status: rawError?.response?.status,
                        headers: rawError?.response?.headers,
                    };
                },
            },
            parameters: {
                requests: [
                    {
                        command: 'list',
                        parameters: {
                            path: '//sys/media',
                            ...constants_1.USE_SUPRESS_SYNC,
                        },
                    },
                    {
                        command: 'get',
                        parameters: {
                            path: '//sys/scheduler/orchid/service/version',
                            ...constants_1.USE_SUPRESS_SYNC,
                        },
                    },
                    {
                        command: 'get',
                        parameters: {
                            path: '//sys/@ui_config',
                            ...constants_1.USE_SUPRESS_SYNC,
                        },
                    },
                    {
                        command: 'get',
                        parameters: {
                            path: '//sys/@ui_config_dev_overrides',
                            ...constants_1.USE_SUPRESS_SYNC,
                        },
                    },
                    ...(!mastersList.output?.length
                        ? []
                        : [
                            {
                                command: 'get',
                                parameters: {
                                    path: `//sys/primary_masters/${mastersList.output[0]}/orchid/service/version`,
                                    ...constants_1.USE_SUPRESS_SYNC,
                                },
                            },
                        ]),
                ],
            },
        })
            .then((response) => {
            cx.log('Fetched cluster config', {
                cluster,
                'x-yt-proxy': response.headers['x-yt-proxy'],
                'x-yt-request-id': response.headers['x-yt-request-id'],
                'x-yt-trace-id': response.headers['x-yt-trace-id'],
            });
            sendStats(response.status);
            return response.data;
        })
            .catch((error) => {
            const headers = error?.headers || {};
            cx.logError('Cluster config fetch failed', {
                cluster,
                'x-yt-proxy': headers['x-yt-proxy'],
                'x-yt-request-id': headers['x-yt-request-id'],
                'x-yt-trace-id': headers['x-yt-trace-id'],
            });
            sendStats(error.status);
            return Promise.reject(error.data);
        });
        if (uiDevConfig?.error?.code === yt.codes.NODE_DOES_NOT_EXIST) {
            delete uiDevConfig.error;
        }
        if (schedulerVersion?.error?.code === yt.codes.NODE_DOES_NOT_EXIST) {
            delete schedulerVersion.error;
            schedulerVersion.output = '0.0.0-unknown';
        }
        if (masterVersion?.error?.code === yt.codes.NODE_DOES_NOT_EXIST) {
            delete masterVersion.error;
            masterVersion.output = '0.0.0-unknown';
        }
        const response = {
            mediumList,
            schedulerVersion,
            uiConfig: outputUiSettingsToCamelCase(uiConfig),
            uiDevConfig: outputUiSettingsToCamelCase(uiDevConfig),
            masterVersion: masterVersion ?? mastersList,
        };
        if (mediumList.error ||
            schedulerVersion.error ||
            (uiConfig.error && uiConfig.error.code !== yt.codes.NODE_DOES_NOT_EXIST) ||
            (uiDevConfig.error && uiDevConfig.error.code !== yt.codes.NODE_DOES_NOT_EXIST) ||
            (masterVersion?.error && masterVersion.error.code !== yt.codes.NODE_DOES_NOT_EXIST) ||
            mastersList.error) {
            cx.logError('Unacceptable response', undefined, {
                cluster,
                mediumList: { error: mediumList.error },
                schedulerVersion: { error: schedulerVersion.error },
                uiConfig: { error: uiConfig.error },
                uiDevConfig: { error: uiDevConfig.error },
                masterVersion: { error: masterVersion?.error },
                masterList: { error: mastersList?.error },
            });
            throw new BatchSubrequestError(response);
        }
        return response;
    });
}
function outputUiSettingsToCamelCase(obj) {
    if (!obj?.output?.ui_settings) {
        return obj;
    }
    return {
        ...obj,
        output: {
            ...obj.output,
            ui_settings: (0, snake_to_camel_1.snakeToCamelObject)(obj.output.ui_settings),
        },
    };
}
const getCached = (0, auto_updated_cache_1.createAutoUpdatedCache)(fetchClusterParams);
async function getPreloadedClusterParams(cluster, ctx) {
    const ctxObject = { ctx };
    let response;
    try {
        response = await getCached(cluster, ctxObject);
    }
    catch (error) {
        if (error instanceof BatchSubrequestError) {
            response = error.response;
        }
        else {
            throw error;
        }
    }
    finally {
        delete ctxObject.ctx;
    }
    return response;
}
async function getPreloadedClusterUiConfig(cluster, ctx, isDeveloper) {
    try {
        const { uiConfig, uiDevConfig } = await getPreloadedClusterParams(cluster, ctx);
        return { ...uiConfig.output, ...(isDeveloper ? uiDevConfig.output : {}) };
    }
    catch {
        return {};
    }
}
