"use strict";
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); }
};
var ClusterCapacityService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClusterCapacityService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const cluster_entity_1 = require("../entities/cluster.entity");
const cluster_node_entity_1 = require("../entities/cluster-node.entity");
const kubernetes_service_1 = require("../../shared/services/kubernetes.service");
const encryption_service_1 = require("../../../shared/encryption/services/encryption.service");
const provider_factory_1 = require("../../../providers/services/provider.factory");
const cluster_storage_service_1 = require("./cluster-storage.service");
let ClusterCapacityService = ClusterCapacityService_1 = class ClusterCapacityService {
    constructor(clusterRepository, nodeRepository, kubernetesService, encryptionService, providerFactory, clusterStorageService) {
        this.clusterRepository = clusterRepository;
        this.nodeRepository = nodeRepository;
        this.kubernetesService = kubernetesService;
        this.encryptionService = encryptionService;
        this.providerFactory = providerFactory;
        this.clusterStorageService = clusterStorageService;
        this.logger = new common_1.Logger(ClusterCapacityService_1.name);
    }
    async getPlan(clusterId) {
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        const masterNode = await this.nodeRepository.findOne({
            where: { clusterId, nodeType: cluster_node_entity_1.NodeType.MASTER },
        });
        const provider = cluster.provider;
        const providerService = this.providerFactory.getProvider(provider);
        let currentServerType;
        if (masterNode?.providerResourceId &&
            providerService.getServerDetailsAsDto) {
            try {
                const details = await providerService.getServerDetailsAsDto(masterNode.providerResourceId);
                currentServerType = details?.server_type;
            }
            catch (err) {
                this.logger.warn(`Failed to fetch master server details for ${masterNode.providerResourceId}: ${err.message}`);
            }
        }
        let sizes = [];
        if (providerService.getNodeSizes) {
            try {
                sizes = await providerService.getNodeSizes(false);
            }
            catch (err) {
                this.logger.warn(`Provider ${provider} getNodeSizes failed: ${err.message}`);
            }
        }
        const currentSize = currentServerType
            ? sizes.find((s) => s.name === currentServerType)
            : undefined;
        let master;
        if (cluster.kubeconfigEncrypted) {
            const kubeconfig = this.encryptionService.decrypt(cluster.kubeconfigEncrypted);
            const cap = await this.kubernetesService.getMasterNodeCapacity(kubeconfig);
            if (cap) {
                master = {
                    nodeName: cap.nodeName,
                    serverType: currentServerType ?? 'unknown',
                    allocatableCpuMillicores: cap.allocatable.cpu,
                    allocatableMemoryMi: cap.allocatable.memory,
                    usedCpuMillicores: cap.requested.cpu,
                    usedMemoryMi: cap.requested.memory,
                    freeCpuMillicores: cap.allocatable.cpu - cap.requested.cpu,
                    freeMemoryMi: cap.allocatable.memory - cap.requested.memory,
                    monthlyCostEur: this.pickFirstMonthlyPrice(currentSize),
                };
            }
        }
        const candidates = this.buildCandidates(sizes, currentSize);
        let storage;
        try {
            const storageStatus = await this.clusterStorageService.getStatus(clusterId);
            if (storageStatus.volume) {
                storage = {
                    volumeId: storageStatus.volume.volumeId,
                    sizeGb: storageStatus.volume.sizeGb,
                    requestedGb: storageStatus.pvcs?.requestedGb,
                    pricePerGbMonthlyEur: currentSize?.blockStoragePricePerGbMonthly,
                };
            }
        }
        catch (err) {
            this.logger.warn(`Storage status unavailable: ${err.message}`);
        }
        return {
            clusterId,
            provider,
            master,
            candidates,
            storage,
            message: this.buildMessage({
                currentServerType,
                sizesCount: sizes.length,
            }),
        };
    }
    buildCandidates(sizes, currentSize) {
        if (!sizes.length)
            return [];
        const currentMonthly = this.parseMonthly(currentSize);
        const candidates = sizes
            .filter((s) => !s.deprecated)
            .map((s) => {
            const monthly = this.parseMonthly(s);
            let direction;
            if (s.name === currentSize?.name) {
                direction = 'current';
            }
            else if (s.cores >= (currentSize?.cores ?? 0) &&
                s.memory >= (currentSize?.memory ?? 0)) {
                direction = 'upgrade';
            }
            else {
                direction = 'downgrade';
            }
            return {
                name: s.name,
                direction,
                cores: s.cores,
                memoryGb: s.memory,
                diskGb: s.disk,
                monthlyCostEur: monthly === null ? 'n/a' : monthly.toFixed(2),
                monthlyDeltaEur: monthly !== null && currentMonthly !== null
                    ? (monthly - currentMonthly).toFixed(2)
                    : 'n/a',
                cpuType: s.cpuType,
                deprecated: s.deprecated,
            };
        });
        candidates.sort((a, b) => {
            const da = Number.parseFloat(a.monthlyDeltaEur);
            const db = Number.parseFloat(b.monthlyDeltaEur);
            if (Number.isNaN(da) && Number.isNaN(db))
                return a.name.localeCompare(b.name);
            if (Number.isNaN(da))
                return 1;
            if (Number.isNaN(db))
                return -1;
            return da - db;
        });
        return candidates;
    }
    pickFirstMonthlyPrice(size) {
        const price = size?.prices?.[0]?.priceMonthly?.net;
        if (!price)
            return undefined;
        const parsed = Number.parseFloat(price);
        return Number.isFinite(parsed) ? parsed.toFixed(2) : undefined;
    }
    parseMonthly(size) {
        const raw = size?.prices?.[0]?.priceMonthly?.net;
        if (!raw)
            return null;
        const v = Number.parseFloat(raw);
        return Number.isFinite(v) ? v : null;
    }
    buildMessage(ctx) {
        if (!ctx.currentServerType) {
            return 'Could not resolve current master server type from provider — costs are shown as n/a.';
        }
        if (ctx.sizesCount === 0) {
            return 'Provider did not return any node sizes — upgrade/downgrade candidates unavailable.';
        }
        return undefined;
    }
};
exports.ClusterCapacityService = ClusterCapacityService;
exports.ClusterCapacityService = ClusterCapacityService = ClusterCapacityService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(cluster_entity_1.ClusterEntity)),
    __param(1, (0, typeorm_1.InjectRepository)(cluster_node_entity_1.ClusterNodeEntity)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        kubernetes_service_1.KubernetesService,
        encryption_service_1.EncryptionService,
        provider_factory_1.ProviderFactory,
        cluster_storage_service_1.ClusterStorageService])
], ClusterCapacityService);
