"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 ScalewayVolumeOperationsService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScalewayVolumeOperationsService = void 0;
const common_1 = require("@nestjs/common");
const kubernetes_service_1 = require("../../../infrastructure/shared/services/kubernetes.service");
const VOLUME_SNAPSHOT_API_VERSION = 'snapshot.storage.k8s.io/v1';
const DEFAULT_VOLUME_SNAPSHOT_CLASS = 'scw-snapshot';
const SCALEWAY_SNAPSHOT_PRICE_PER_GB_HOUR_EUR = 0.000044;
const SNAPSHOT_READY_TIMEOUT_SECONDS = 5 * 60;
/**
 * Scaleway Block Storage exposes CSI VolumeSnapshot via the scaleway-csi
 * driver (v0.3+) backed by the SBS API. Snapshots are first-class objects
 * billed separately from volumes (~€0.000044/GB-hour ≈ €0.032/GB-mo) and
 * can be retained as long as needed.
 */
let ScalewayVolumeOperationsService = ScalewayVolumeOperationsService_1 = class ScalewayVolumeOperationsService {
    constructor(k8s) {
        this.k8s = k8s;
        this.logger = new common_1.Logger(ScalewayVolumeOperationsService_1.name);
        this.capabilities = {
            supportsSnapshotRetention: true,
            supportsCsiSnapshot: true,
            requiresCopyPodFallback: false,
            snapshotCostPerGbMonthEur: parseFloat((SCALEWAY_SNAPSHOT_PRICE_PER_GB_HOUR_EUR * 24 * 30).toFixed(4)),
        };
    }
    async createSnapshot(input) {
        const sourcePvc = await this.k8s.getResource(input.kubeconfig, 'PersistentVolumeClaim', input.sourcePvcName, input.namespace);
        if (!sourcePvc) {
            throw new Error(`Source PVC ${input.namespace}/${input.sourcePvcName} not found`);
        }
        const sizeGb = this.parseStorageGb(sourcePvc?.spec?.resources?.requests?.storage ?? '0');
        const manifest = this.renderSnapshotManifest({
            name: input.snapshotName,
            namespace: input.namespace,
            sourcePvcName: input.sourcePvcName,
            labels: input.labels,
            snapshotClass: DEFAULT_VOLUME_SNAPSHOT_CLASS,
        });
        await this.k8s.applyManifest(input.kubeconfig, manifest);
        this.logger.log(`[scaleway-snapshot] Created VolumeSnapshot ${input.namespace}/${input.snapshotName} from ${input.sourcePvcName}`);
        const ready = await this.waitForSnapshotReady(input.kubeconfig, input.namespace, input.snapshotName, SNAPSHOT_READY_TIMEOUT_SECONDS);
        return {
            snapshotId: input.snapshotName,
            kind: 'csi-snapshot',
            namespace: input.namespace,
            sourceSizeGb: sizeGb,
            createdAt: new Date().toISOString(),
            ready,
        };
    }
    async deleteSnapshot(input) {
        try {
            await this.k8s.deleteResource(input.kubeconfig, 'VolumeSnapshot', input.snapshotId, input.namespace);
        }
        catch (err) {
            if (input.ignoreNotFound && err?.message?.includes('not found')) {
                return;
            }
            throw err;
        }
    }
    async listSnapshots(input) {
        const namespace = input.namespace ?? '';
        if (!namespace) {
            this.logger.debug('[scaleway-snapshot] cluster-wide list requires namespace iteration; returning empty result. Caller should pass per-namespace queries.');
            return [];
        }
        const items = await this.k8s.listResourcesByLabel(input.kubeconfig, 'VolumeSnapshot', namespace, input.labelSelector ?? '');
        return items.map((snap) => {
            const labels = snap?.metadata?.labels ?? {};
            const restoreSize = snap?.status?.restoreSize;
            const sizeGb = restoreSize ? this.parseStorageGb(restoreSize) : undefined;
            const sourcePvcName = snap?.spec?.source?.persistentVolumeClaimName;
            return {
                snapshotId: snap?.metadata?.name,
                kind: 'csi-snapshot',
                namespace: snap?.metadata?.namespace,
                sourcePvcName,
                appId: labels['flui-app-id'],
                sizeGb,
                createdAt: snap?.metadata?.creationTimestamp ??
                    new Date().toISOString(),
                ready: Boolean(snap?.status?.readyToUse),
                labels,
            };
        });
    }
    async createPvcFromSnapshot(input) {
        const labelLines = Object.entries(input.labels)
            .map(([k, v]) => `    ${this.yamlString(k)}: ${this.yamlString(v)}`)
            .join('\n');
        const manifest = [
            'apiVersion: v1',
            'kind: PersistentVolumeClaim',
            'metadata:',
            `  name: ${input.newPvcName}`,
            `  namespace: ${input.namespace}`,
            '  labels:',
            labelLines,
            'spec:',
            '  accessModes:',
            '    - ReadWriteOnce',
            `  storageClassName: ${input.storageClassName}`,
            '  resources:',
            '    requests:',
            `      storage: ${input.sizeGb}Gi`,
            '  dataSource:',
            `    name: ${input.snapshotId}`,
            '    kind: VolumeSnapshot',
            '    apiGroup: snapshot.storage.k8s.io',
            '',
        ].join('\n');
        await this.k8s.applyManifest(input.kubeconfig, manifest);
        return { pvcName: input.newPvcName };
    }
    async waitForSnapshotReady(kubeconfig, namespace, snapshotName, timeoutSeconds) {
        const pollIntervalMs = 3000;
        const start = Date.now();
        while (Date.now() - start < timeoutSeconds * 1000) {
            const snap = await this.k8s.getResource(kubeconfig, 'VolumeSnapshot', snapshotName, namespace);
            if (snap?.status?.readyToUse)
                return true;
            if (snap?.status?.error) {
                const msg = snap.status.error.message ?? 'unknown';
                throw new Error(`VolumeSnapshot ${namespace}/${snapshotName} reported error: ${msg}`);
            }
            await new Promise((r) => setTimeout(r, pollIntervalMs));
        }
        this.logger.warn(`[scaleway-snapshot] VolumeSnapshot ${namespace}/${snapshotName} not ready within ${timeoutSeconds}s — caller can poll separately`);
        return false;
    }
    renderSnapshotManifest(args) {
        const labelLines = Object.entries(args.labels)
            .map(([k, v]) => `    ${this.yamlString(k)}: ${this.yamlString(v)}`)
            .join('\n');
        return [
            `apiVersion: ${VOLUME_SNAPSHOT_API_VERSION}`,
            'kind: VolumeSnapshot',
            'metadata:',
            `  name: ${args.name}`,
            `  namespace: ${args.namespace}`,
            '  labels:',
            labelLines,
            'spec:',
            `  volumeSnapshotClassName: ${args.snapshotClass}`,
            '  source:',
            `    persistentVolumeClaimName: ${args.sourcePvcName}`,
            '',
        ].join('\n');
    }
    parseStorageGb(value) {
        if (!value)
            return 0;
        const match = String(value).match(/^(\d+(?:\.\d+)?)([KMGTP]i?)?$/);
        if (!match)
            return 0;
        const num = Number.parseFloat(match[1]);
        const unit = match[2];
        switch (unit) {
            case 'Ki':
                return num / (1024 * 1024);
            case 'Mi':
                return num / 1024;
            case 'Gi':
                return num;
            case 'Ti':
                return num * 1024;
            case 'K':
                return num / 1_000_000;
            case 'M':
                return num / 1000;
            case 'G':
                return num;
            case 'T':
                return num * 1000;
            default:
                return num;
        }
    }
    yamlString(value) {
        return JSON.stringify(value);
    }
};
exports.ScalewayVolumeOperationsService = ScalewayVolumeOperationsService;
exports.ScalewayVolumeOperationsService = ScalewayVolumeOperationsService = ScalewayVolumeOperationsService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [kubernetes_service_1.KubernetesService])
], ScalewayVolumeOperationsService);
