"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 BillingIntervalsService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BillingIntervalsService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const node_billable_interval_entity_1 = require("../entities/node-billable-interval.entity");
const volume_billable_interval_entity_1 = require("../entities/volume-billable-interval.entity");
const cluster_node_entity_1 = require("../entities/cluster-node.entity");
let BillingIntervalsService = BillingIntervalsService_1 = class BillingIntervalsService {
    constructor(nodeIntervalRepo, volumeIntervalRepo) {
        this.nodeIntervalRepo = nodeIntervalRepo;
        this.volumeIntervalRepo = volumeIntervalRepo;
        this.logger = new common_1.Logger(BillingIntervalsService_1.name);
    }
    async openNodeInterval(input) {
        try {
            await this.closeNodeIntervals(input.nodeId, input.startedAt ?? new Date());
            const entity = this.nodeIntervalRepo.create({
                clusterId: input.clusterId,
                nodeId: input.nodeId,
                serverName: input.serverName,
                providerResourceId: input.providerResourceId,
                provider: input.provider,
                region: input.region,
                location: input.location,
                serverType: input.serverType,
                nodeType: input.nodeType,
                startedAt: input.startedAt ?? new Date(),
                endedAt: null,
                metadata: input.metadata ?? {},
            });
            await this.nodeIntervalRepo.save(entity);
        }
        catch (err) {
            this.logger.warn(`openNodeInterval failed for node ${input.nodeId}: ${err.message}`);
        }
    }
    async closeNodeIntervals(nodeId, at = new Date()) {
        try {
            await this.nodeIntervalRepo.update({ nodeId, endedAt: (0, typeorm_2.IsNull)() }, { endedAt: at });
        }
        catch (err) {
            this.logger.warn(`closeNodeIntervals failed for node ${nodeId}: ${err.message}`);
        }
    }
    async openVolumeInterval(input) {
        try {
            await this.closeVolumeIntervals(input.volumeProviderId, input.startedAt ?? new Date());
            const entity = this.volumeIntervalRepo.create({
                clusterId: input.clusterId,
                volumeProviderId: input.volumeProviderId,
                provider: input.provider,
                region: input.region,
                kind: input.kind,
                sizeGb: input.sizeGb,
                startedAt: input.startedAt ?? new Date(),
                endedAt: null,
                metadata: input.metadata ?? {},
            });
            await this.volumeIntervalRepo.save(entity);
        }
        catch (err) {
            this.logger.warn(`openVolumeInterval failed for volume ${input.volumeProviderId}: ${err.message}`);
        }
    }
    async closeVolumeIntervals(volumeProviderId, at = new Date()) {
        try {
            await this.volumeIntervalRepo.update({ volumeProviderId, endedAt: (0, typeorm_2.IsNull)() }, { endedAt: at });
        }
        catch (err) {
            this.logger.warn(`closeVolumeIntervals failed for ${volumeProviderId}: ${err.message}`);
        }
    }
    /**
     * Backfill: for every node and shared volume of every cluster without an
     * open interval, open one starting at the resource's createdAt. Idempotent —
     * called on app startup so pre-existing clusters become billable.
     */
    async backfillFromClusters(clusters) {
        let nodesOpened = 0;
        let volumesOpened = 0;
        for (const cluster of clusters) {
            for (const node of cluster.nodes ?? []) {
                const existing = await this.nodeIntervalRepo.findOne({
                    where: { nodeId: node.id },
                });
                if (existing)
                    continue;
                try {
                    await this.nodeIntervalRepo.save(this.nodeIntervalRepo.create({
                        clusterId: cluster.id,
                        nodeId: node.id,
                        serverName: node.serverName,
                        providerResourceId: node.providerResourceId,
                        provider: cluster.provider,
                        region: cluster.region,
                        serverType: cluster.nodeSize,
                        nodeType: node.nodeType,
                        startedAt: node.createdAt,
                        endedAt: null,
                        metadata: { backfilled: true },
                    }));
                    nodesOpened++;
                }
                catch (err) {
                    this.logger.warn(`backfill node ${node.id} failed: ${err.message}`);
                }
            }
            if (cluster.sharedStorageVolumeId) {
                const existingVol = await this.volumeIntervalRepo.findOne({
                    where: { volumeProviderId: cluster.sharedStorageVolumeId },
                });
                if (!existingVol) {
                    try {
                        await this.volumeIntervalRepo.save(this.volumeIntervalRepo.create({
                            clusterId: cluster.id,
                            volumeProviderId: cluster.sharedStorageVolumeId,
                            provider: cluster.provider,
                            region: cluster.region,
                            kind: volume_billable_interval_entity_1.VolumeBillableKind.SHARED_STORAGE,
                            sizeGb: cluster.sharedStorageVolumeSizeGb ?? 0,
                            startedAt: cluster.createdAt,
                            endedAt: null,
                            metadata: { backfilled: true },
                        }));
                        volumesOpened++;
                    }
                    catch (err) {
                        this.logger.warn(`backfill volume ${cluster.sharedStorageVolumeId} failed: ${err.message}`);
                    }
                }
            }
        }
        return { nodes: nodesOpened, volumes: volumesOpened };
    }
};
exports.BillingIntervalsService = BillingIntervalsService;
exports.BillingIntervalsService = BillingIntervalsService = BillingIntervalsService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(node_billable_interval_entity_1.NodeBillableIntervalEntity)),
    __param(1, (0, typeorm_1.InjectRepository)(volume_billable_interval_entity_1.VolumeBillableIntervalEntity)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository])
], BillingIntervalsService);
void cluster_node_entity_1.NodeType;
