// SPDX-License-Identifier: Apache-2.0
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { PodMetrics } from '../model/pod-metrics.js';
import { NamespaceName } from '../../../types/namespace/namespace-name.js';
import { ShellRunner } from '../../../core/shell-runner.js';
import { PodName } from '../../../integration/kube/resources/pod/pod-name.js';
import { inject, injectable } from 'tsyringe-neo';
import { patchInject } from '../../../core/dependency-injection/container-helper.js';
import { InjectTokens } from '../../../core/dependency-injection/inject-tokens.js';
import fs from 'node:fs';
import { AggregatedMetrics } from '../model/aggregated-metrics.js';
import { ClusterMetrics } from '../model/cluster-metrics.js';
import { ContainerReference } from '../../../integration/kube/resources/container/container-reference.js';
import { ContainerName } from '../../../integration/kube/resources/container/container-name.js';
import { PodReference } from '../../../integration/kube/resources/pod/pod-reference.js';
import { container } from 'tsyringe-neo';
import { Duration } from '../../../core/time/duration.js';
let MetricsServerImpl = class MetricsServerImpl {
    logger;
    k8Factory;
    ignorePodMetrics;
    constructor(logger, k8Factory, ignorePodMetrics) {
        this.logger = logger;
        this.k8Factory = k8Factory;
        this.ignorePodMetrics = ignorePodMetrics;
        this.logger = patchInject(logger, InjectTokens.SoloLogger, this.constructor.name);
        this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, this.constructor.name);
        this.ignorePodMetrics = patchInject(ignorePodMetrics, InjectTokens.IgnorePodMetrics, this.constructor.name);
    }
    async getMetrics(snapshotName, namespaceLookup = undefined, labelSelector = undefined, contexts = undefined, events = []) {
        const clusterMetrics = [];
        if (!contexts || contexts?.length === 0) {
            const clusterMetric = await this.getClusterMetrics(namespaceLookup, labelSelector);
            if (clusterMetric) {
                clusterMetrics.push(clusterMetric);
            }
        }
        else {
            for (const context of contexts) {
                const clusterMetric = await this.getClusterMetrics(namespaceLookup, labelSelector, context);
                if (clusterMetric) {
                    clusterMetrics.push(clusterMetric);
                }
            }
        }
        return this.createAggregatedMetrics(snapshotName, clusterMetrics, events);
    }
    async getClusterMetrics(namespaceLookup = undefined, labelSelector = undefined, context = undefined, attempt = 1) {
        let podMetrics = [];
        const namespaceParameter = namespaceLookup ? `-n ${namespaceLookup.name}` : '-A';
        const contextParameter = context ? `--context ${context}` : '';
        const labelSelectorParameter = labelSelector ? `-l='${labelSelector}'` : '';
        const cmd = `kubectl top pod ${namespaceParameter} --no-headers=true ${contextParameter} ${labelSelectorParameter}`;
        try {
            const results = await new ShellRunner().run(cmd, [], true, false);
            const joinedResults = results.join('\n');
            let namespace;
            let podName;
            let cpuInMillicores;
            let memoryInMebibytes;
            let index = 0;
            let clusterNamespace = '';
            let mirrorNodePostgresPodName = undefined;
            const resultArray = joinedResults
                .trim()
                .split(/\r?\n|\n| +/)
                .filter((c) => c !== '');
            while (index < resultArray.length) {
                namespace = resultArray[index++];
                podName = resultArray[index++];
                cpuInMillicores = +resultArray[index++].split('m')[0];
                memoryInMebibytes = +resultArray[index++].split('Mi')[0];
                podMetrics.push(new PodMetrics(NamespaceName.of(namespace), PodName.of(podName), cpuInMillicores, memoryInMebibytes));
                if (podName.startsWith('network-node1-0')) {
                    clusterNamespace = namespace;
                }
                if (podName.includes('postgres')) {
                    mirrorNodePostgresPodName = podName;
                }
            }
            podMetrics = podMetrics.filter((podMetric) => {
                for (const ignorePattern of this.ignorePodMetrics) {
                    if (podMetric.podName.name.includes(ignorePattern)) {
                        return false;
                    }
                }
                return true;
            });
            return this.createClusterMetrics(context ?? 'default', clusterNamespace ? NamespaceName.of(clusterNamespace) : undefined, podMetrics, mirrorNodePostgresPodName ? PodName.of(mirrorNodePostgresPodName) : undefined);
        }
        catch (error) {
            if (error.message.includes('Metrics API not available')) {
                if (attempt <= 3) {
                    const backOffSeconds = 5;
                    this.logger.debug(`Metrics API not available, retrying attempt ${attempt} after ${backOffSeconds} seconds...`, error);
                    await new Promise((resolve) => setTimeout(resolve, Duration.ofSeconds(backOffSeconds).toMillis()));
                    return this.getClusterMetrics(namespaceLookup, labelSelector, context, attempt + 1);
                }
                else {
                    this.logger.showUser('Metrics API not available for reporting metrics');
                    return undefined;
                }
            }
            throw error;
        }
    }
    async createAggregatedMetrics(snapshotName, clusterMetrics, events = []) {
        let namespace = undefined;
        if (!clusterMetrics || clusterMetrics?.length === 0) {
            return undefined;
        }
        let cpuInMillicores = 0;
        let memoryInMebibytes = 0;
        let runtime = 0;
        let transactions = 0;
        for (const clusterMetric of clusterMetrics) {
            cpuInMillicores += clusterMetric.cpuInMillicores;
            memoryInMebibytes += clusterMetric.memoryInMebibytes;
            runtime += await this.getNetworkNodeRuntime(clusterMetric.namespace, clusterMetric.context);
            transactions += await this.getNetworkTransactions(clusterMetric.namespace, clusterMetric.context, clusterMetric.postgresPodName);
            namespace = clusterMetric.namespace?.name ? clusterMetric.namespace : namespace;
        }
        const remoteConfigRuntimeState = container.resolve(InjectTokens.RemoteConfigRuntimeState);
        if (namespace && namespace.name) {
            await remoteConfigRuntimeState.load(namespace);
        }
        return new AggregatedMetrics(snapshotName, clusterMetrics, cpuInMillicores, memoryInMebibytes, runtime, transactions, events);
    }
    createClusterMetrics(context, namespace, podMetrics, mirrorNodePostgresPodName) {
        if (!podMetrics || podMetrics?.length === 0) {
            return undefined;
        }
        let cpuInMillicores = 0;
        let memoryInMebibytes = 0;
        for (const podMetric of podMetrics) {
            cpuInMillicores += podMetric.cpuInMillicores;
            memoryInMebibytes += podMetric.memoryInMebibytes;
        }
        return new ClusterMetrics(context, namespace, podMetrics, mirrorNodePostgresPodName, cpuInMillicores, memoryInMebibytes);
    }
    async logMetrics(snapshotName, metricsLogFile, namespace, labelSelector, contexts, events = []) {
        const aggregatedMetrics = await this.getMetrics(snapshotName, namespace, labelSelector, contexts, events);
        fs.writeFileSync(`${metricsLogFile}.json`, aggregatedMetrics ? aggregatedMetrics.toString() : '');
    }
    async getNetworkNodeRuntime(namespace, context) {
        if (!namespace) {
            return 0;
        }
        const contextParameter = context && context !== 'default' ? `--context ${context}` : '';
        const cmd = `kubectl get pod network-node1-0 -n ${namespace.name} --no-headers ${contextParameter} | awk '{print $5}'`;
        const results = await new ShellRunner().run(cmd, [], true, false);
        if (results?.length > 0) {
            return Number.parseInt(results[0].split('m')[0]);
        }
        return 0;
    }
    async getNetworkTransactions(namespace, context, postgresPodName) {
        if (!namespace) {
            return 0;
        }
        try {
            const result = await this.k8Factory
                .getK8(context && context !== 'default' ? context : undefined)
                .containers()
                .readByRef(ContainerReference.of(PodReference.of(namespace, postgresPodName), ContainerName.of('postgresql')))
                .execContainer([
                'bash',
                '-c',
                "PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE) psql -U postgres -d mirror_node -c 'select count(*) from transaction;' -t",
            ]);
            return Number.parseInt(result.trim());
        }
        catch (error) {
            this.logger.debug(`error looking up transactions: ${error.message}`, error);
        }
        return 0;
    }
};
MetricsServerImpl = __decorate([
    injectable(),
    __param(0, inject(InjectTokens.SoloLogger)),
    __param(1, inject(InjectTokens.K8Factory)),
    __param(2, inject(InjectTokens.IgnorePodMetrics)),
    __metadata("design:paramtypes", [Object, Object, Array])
], MetricsServerImpl);
export { MetricsServerImpl };
//# sourceMappingURL=metrics-server-impl.js.map