"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 __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 __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
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 ClusterStorageService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClusterStorageService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const k8s = __importStar(require("@kubernetes/client-node"));
const cluster_entity_1 = require("../entities/cluster.entity");
const kubernetes_service_1 = require("../../shared/services/kubernetes.service");
const encryption_service_1 = require("../../../shared/encryption/services/encryption.service");
const cluster_storage_dto_1 = require("../dto/cluster-storage.dto");
const storage_conventions_1 = require("../constants/storage-conventions");
let ClusterStorageService = ClusterStorageService_1 = class ClusterStorageService {
    constructor(clusterRepository, kubernetesService, encryptionService) {
        this.clusterRepository = clusterRepository;
        this.kubernetesService = kubernetesService;
        this.encryptionService = encryptionService;
        this.logger = new common_1.Logger(ClusterStorageService_1.name);
    }
    async getStatus(clusterId) {
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        const enabled = cluster.sharedStorageEnabled !== false;
        const result = {
            clusterId: cluster.id,
            enabled,
            status: cluster_storage_dto_1.ClusterStorageStatus.DISABLED,
        };
        if (!enabled) {
            result.message = 'Shared storage disabled at cluster creation';
            return result;
        }
        const volume = cluster.sharedStorageVolumeId
            ? {
                provider: cluster.provider,
                volumeId: cluster.sharedStorageVolumeId,
                sizeGb: cluster.sharedStorageVolumeSizeGb ?? 0,
                mountPath: storage_conventions_1.FLUI_SHARED_STORAGE_PATH,
                fsLabel: storage_conventions_1.FLUI_SHARED_VOLUME_FS_LABEL,
            }
            : undefined;
        const nfs = {
            exportPath: storage_conventions_1.FLUI_SHARED_STORAGE_PATH,
            exportOptions: storage_conventions_1.NFS_EXPORT_OPTIONS,
            mountOptions: storage_conventions_1.NFS_MOUNT_OPTIONS,
        };
        result.volume = volume;
        result.nfs = nfs;
        if (cluster.status !== cluster_entity_1.ClusterStatus.READY) {
            result.status = cluster_storage_dto_1.ClusterStorageStatus.PROVISIONING;
            result.message = `Cluster is in status ${cluster.status} — storage layer not yet observable`;
            return result;
        }
        try {
            result.pvcs = await this.collectPvcSummary(cluster);
            result.status = cluster_storage_dto_1.ClusterStorageStatus.READY;
        }
        catch (err) {
            this.logger.warn(`[ClusterStorage] PVC summary failed for cluster ${cluster.id}: ${err.message}`);
            result.status = cluster_storage_dto_1.ClusterStorageStatus.DEGRADED;
            result.message = `Could not query PVCs: ${err.message}`;
        }
        return result;
    }
    async collectPvcSummary(cluster) {
        if (!cluster.kubeconfigEncrypted) {
            throw new Error('kubeconfig not available for this cluster');
        }
        const kubeconfig = this.encryptionService.decrypt(cluster.kubeconfigEncrypted);
        const kc = this.kubernetesService.makeKubeConfig(kubeconfig);
        const coreApi = kc.makeApiClient(k8s.CoreV1Api);
        const response = await coreApi.listPersistentVolumeClaimForAllNamespaces();
        const items = response.items ?? response.body?.items ?? [];
        let bound = 0;
        let requestedBytes = 0;
        const byNamespace = {};
        for (const item of items) {
            const phase = item?.status?.phase;
            if (phase !== 'Bound')
                continue;
            const sc = item?.spec?.storageClassName;
            if (sc && sc !== storage_conventions_1.FluiStorageClass.SHARED && sc !== '')
                continue;
            bound++;
            const ns = item?.metadata?.namespace || 'default';
            byNamespace[ns] = (byNamespace[ns] || 0) + 1;
            const requested = item?.spec?.resources?.requests?.storage;
            if (requested) {
                requestedBytes += this.parseQuantityToBytes(requested);
            }
        }
        return {
            bound,
            requestedGb: Math.round((requestedBytes / 1_000_000_000) * 100) / 100,
            byNamespace,
        };
    }
    parseQuantityToBytes(quantity) {
        const m = /^(\d+(?:\.\d+)?)([KMGTPE]i?)?$/.exec(quantity);
        if (!m)
            return 0;
        const value = Number.parseFloat(m[1]);
        const unit = m[2] || '';
        const map = {
            '': 1,
            K: 1e3,
            M: 1e6,
            G: 1e9,
            T: 1e12,
            P: 1e15,
            E: 1e18,
            Ki: 1024,
            Mi: 1024 ** 2,
            Gi: 1024 ** 3,
            Ti: 1024 ** 4,
            Pi: 1024 ** 5,
            Ei: 1024 ** 6,
        };
        return value * (map[unit] ?? 1);
    }
};
exports.ClusterStorageService = ClusterStorageService;
exports.ClusterStorageService = ClusterStorageService = ClusterStorageService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(cluster_entity_1.ClusterEntity)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        kubernetes_service_1.KubernetesService,
        encryption_service_1.EncryptionService])
], ClusterStorageService);
