"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 ClusterPowerManagementService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClusterPowerManagementService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const bull_1 = require("@nestjs/bull");
const cluster_entity_1 = require("../entities/cluster.entity");
const provider_factory_1 = require("../../../../modules/providers/services/provider.factory");
const infrastructure_operations_entity_1 = require("../../servers/entities/infrastructure-operations.entity");
const operation_steps_helper_1 = require("../../operations/helpers/operation-steps.helper");
/**
 * Service for managing cluster power state (stop/start servers)
 * and reconciling cluster status with real provider state
 */
let ClusterPowerManagementService = ClusterPowerManagementService_1 = class ClusterPowerManagementService {
    constructor(clusterRepository, operationRepository, infrastructureQueue, providerFactory) {
        this.clusterRepository = clusterRepository;
        this.operationRepository = operationRepository;
        this.infrastructureQueue = infrastructureQueue;
        this.providerFactory = providerFactory;
        this.logger = new common_1.Logger(ClusterPowerManagementService_1.name);
    }
    /**
     * Stop all servers in a cluster (async - queued operation)
     */
    async stopClusterAsync(clusterId) {
        this.logger.log(`Initiating async stop for cluster ${clusterId}`);
        // Validate cluster exists
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
            relations: ['nodes'],
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        if (!cluster.nodes || cluster.nodes.length === 0) {
            throw new common_1.BadRequestException('Cluster has no nodes');
        }
        // Check provider supports power management
        await this.getProviderWithPowerManagement(cluster);
        // Create operation steps
        const nodeCount = cluster.nodes.length;
        const steps = (0, operation_steps_helper_1.getOperationSteps)(infrastructure_operations_entity_1.OperationType.STOP_CLUSTER, {
            nodeCount,
        });
        // Create operation entity
        const operation = this.operationRepository.create({
            operationType: infrastructure_operations_entity_1.OperationType.STOP_CLUSTER,
            resourceType: 'cluster',
            resourceId: clusterId,
            status: infrastructure_operations_entity_1.OperationStatus.PENDING,
            progress: 0,
            currentStepIndex: 0,
            totalSteps: steps.length,
            metadata: {
                operationSteps: steps,
                clusterName: cluster.name,
                nodeCount: cluster.nodes.length,
                estimatedDurationInSeconds: 60 + nodeCount * 30, // ~30s per node
            },
        });
        await this.operationRepository.save(operation);
        // Add job to queue
        const jobData = {
            operationId: operation.id,
            clusterId,
        };
        await this.infrastructureQueue.add('stop-cluster', jobData, {
            attempts: 3,
            backoff: {
                type: 'exponential',
                delay: 2000,
            },
            timeout: 600000, // 10 minutes
        });
        this.logger.log(`Stop cluster operation queued: ${operation.id} for cluster ${clusterId}`);
        return {
            operation_id: operation.id,
            cluster_id: clusterId,
            status: 'pending',
            estimated_duration: `${Math.ceil((60 + nodeCount * 30) / 60)} minutes`,
            created_at: operation.createdAt,
        };
    }
    /**
     * Start all servers in a cluster (async - queued operation)
     */
    async startClusterAsync(clusterId) {
        this.logger.log(`Initiating async start for cluster ${clusterId}`);
        // Validate cluster exists
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
            relations: ['nodes'],
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        if (!cluster.nodes || cluster.nodes.length === 0) {
            throw new common_1.BadRequestException('Cluster has no nodes');
        }
        // Check provider supports power management
        await this.getProviderWithPowerManagement(cluster);
        // Create operation steps
        const nodeCount = cluster.nodes.length;
        const steps = (0, operation_steps_helper_1.getOperationSteps)(infrastructure_operations_entity_1.OperationType.START_CLUSTER, {
            nodeCount,
        });
        // Create operation entity
        const operation = this.operationRepository.create({
            operationType: infrastructure_operations_entity_1.OperationType.START_CLUSTER,
            resourceType: 'cluster',
            resourceId: clusterId,
            status: infrastructure_operations_entity_1.OperationStatus.PENDING,
            progress: 0,
            currentStepIndex: 0,
            totalSteps: steps.length,
            metadata: {
                operationSteps: steps,
                clusterName: cluster.name,
                nodeCount: cluster.nodes.length,
                estimatedDurationInSeconds: 120 + nodeCount * 45, // ~45s per node + boot time
            },
        });
        await this.operationRepository.save(operation);
        // Add job to queue
        const jobData = {
            operationId: operation.id,
            clusterId,
        };
        await this.infrastructureQueue.add('start-cluster', jobData, {
            attempts: 3,
            backoff: {
                type: 'exponential',
                delay: 2000,
            },
            timeout: 600000, // 10 minutes
        });
        this.logger.log(`Start cluster operation queued: ${operation.id} for cluster ${clusterId}`);
        return {
            operation_id: operation.id,
            cluster_id: clusterId,
            status: 'pending',
            estimated_duration: `${Math.ceil((120 + nodeCount * 45) / 60)} minutes`,
            created_at: operation.createdAt,
        };
    }
    /**
     * Execute stop cluster operation (called by processor)
     * @deprecated Use stopCluster for sync operations
     */
    async stopCluster(clusterId) {
        this.logger.log(`Stopping cluster ${clusterId}`);
        // Get cluster with nodes
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
            relations: ['nodes'],
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        if (!cluster.nodes || cluster.nodes.length === 0) {
            throw new common_1.BadRequestException('Cluster has no nodes');
        }
        // Check if already stopped
        if (cluster.status === cluster_entity_1.ClusterStatus.STOPPED) {
            this.logger.log(`Cluster ${clusterId} is already stopped`);
            return {
                cluster_id: cluster.id,
                cluster_name: cluster.name,
                status: cluster_entity_1.ClusterStatus.STOPPED,
                servers_stopped: 0,
                total_servers: cluster.nodes.length,
                monthly_savings_estimate: this.calculateSavings(cluster.nodes.length),
            };
        }
        // Get provider
        const provider = await this.getProviderWithPowerManagement(cluster);
        // Stop all servers
        let stoppedCount = 0;
        for (const node of cluster.nodes) {
            try {
                // Check current server status
                const serverDto = await provider.getServerDetailsAsDto(node.providerResourceId);
                if (serverDto && serverDto.status !== 'off') {
                    this.logger.log(`Powering off server ${node.serverName} (${node.providerResourceId})`);
                    await provider.powerOffServer(node.providerResourceId);
                    stoppedCount++;
                }
                else {
                    this.logger.log(`Server ${node.serverName} is already off`);
                }
            }
            catch (error) {
                this.logger.error(`Failed to stop server ${node.serverName}: ${error.message}`);
                throw new common_1.BadRequestException(`Failed to stop server ${node.serverName}: ${error.message}`);
            }
        }
        // Update cluster status
        cluster.status = cluster_entity_1.ClusterStatus.STOPPED;
        await this.clusterRepository.save(cluster);
        this.logger.log(`Successfully stopped ${stoppedCount} servers in cluster ${clusterId}`);
        return {
            cluster_id: cluster.id,
            cluster_name: cluster.name,
            status: cluster_entity_1.ClusterStatus.STOPPED,
            servers_stopped: stoppedCount,
            total_servers: cluster.nodes.length,
            monthly_savings_estimate: this.calculateSavings(cluster.nodes.length),
        };
    }
    /**
     * Start all servers in a cluster
     */
    async startCluster(clusterId) {
        this.logger.log(`Starting cluster ${clusterId}`);
        // Get cluster with nodes
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
            relations: ['nodes'],
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        if (!cluster.nodes || cluster.nodes.length === 0) {
            throw new common_1.BadRequestException('Cluster has no nodes');
        }
        // Check if already running
        if (cluster.status === cluster_entity_1.ClusterStatus.READY) {
            this.logger.log(`Cluster ${clusterId} is already running`);
            return {
                cluster_id: cluster.id,
                cluster_name: cluster.name,
                status: cluster_entity_1.ClusterStatus.READY,
                servers_started: 0,
                total_servers: cluster.nodes.length,
            };
        }
        // Get provider
        const provider = await this.getProviderWithPowerManagement(cluster);
        // Start all servers
        let startedCount = 0;
        for (const node of cluster.nodes) {
            try {
                // Check current server status
                const serverDto = await provider.getServerDetailsAsDto(node.providerResourceId);
                if (serverDto?.status === 'off') {
                    this.logger.log(`Powering on server ${node.serverName} (${node.providerResourceId})`);
                    await provider.powerOnServer(node.providerResourceId);
                    startedCount++;
                }
                else {
                    this.logger.log(`Server ${node.serverName} is already running`);
                }
            }
            catch (error) {
                this.logger.error(`Failed to start server ${node.serverName}: ${error.message}`);
                throw new common_1.BadRequestException(`Failed to start server ${node.serverName}: ${error.message}`);
            }
        }
        // Update cluster status
        cluster.status = cluster_entity_1.ClusterStatus.READY;
        await this.clusterRepository.save(cluster);
        this.logger.log(`Successfully started ${startedCount} servers in cluster ${clusterId}`);
        return {
            cluster_id: cluster.id,
            cluster_name: cluster.name,
            status: cluster_entity_1.ClusterStatus.READY,
            servers_started: startedCount,
            total_servers: cluster.nodes.length,
        };
    }
    /**
     * Reconcile cluster status with real provider state
     */
    async reconcileClusterStatus(clusterId, autoFix = false) {
        this.logger.log(`Reconciling cluster status for ${clusterId} (autoFix: ${autoFix})`);
        const cluster = await this.loadClusterWithNodes(clusterId);
        const previousStatus = cluster.status;
        const actions = [];
        const provider = await this.getProviderWithPowerManagement(cluster);
        const counts = await this.countNodeStates(cluster, provider);
        const totalNodes = cluster.nodes.length;
        const reconciled = await this.computeReconciliation(cluster, counts, totalNodes, autoFix, actions, clusterId);
        if (reconciled.newStatus !== cluster.status) {
            cluster.status = reconciled.newStatus;
            await this.clusterRepository.save(cluster);
        }
        this.logger.log(`Reconciliation complete for cluster ${clusterId}. Actions: ${actions.length}`);
        return {
            cluster_id: cluster.id,
            cluster_name: cluster.name,
            previous_status: previousStatus,
            new_status: reconciled.newStatus,
            is_synced: reconciled.isSynced,
            nodes_reconciled: totalNodes,
            actions_taken: actions,
        };
    }
    async loadClusterWithNodes(clusterId) {
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
            relations: ['nodes'],
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        if (!cluster.nodes || cluster.nodes.length === 0) {
            throw new common_1.BadRequestException('Cluster has no nodes');
        }
        return cluster;
    }
    async countNodeStates(cluster, provider) {
        let runningCount = 0;
        let stoppedCount = 0;
        let otherCount = 0;
        for (const node of cluster.nodes) {
            try {
                const serverDto = await provider.getServerDetailsAsDto(node.providerResourceId);
                if (!serverDto)
                    continue;
                if (serverDto.status === 'running')
                    runningCount++;
                else if (serverDto.status === 'off')
                    stoppedCount++;
                else
                    otherCount++;
            }
            catch (error) {
                this.logger.warn(`Could not check status for node ${node.serverName}: ${error.message}`);
                otherCount++;
            }
        }
        return { runningCount, stoppedCount, otherCount };
    }
    async computeReconciliation(cluster, counts, totalNodes, autoFix, actions, clusterId) {
        const { runningCount, stoppedCount, otherCount } = counts;
        if (stoppedCount === totalNodes) {
            if (cluster.status !== cluster_entity_1.ClusterStatus.STOPPED) {
                actions.push(`Updated cluster status from ${cluster.status} to ${cluster_entity_1.ClusterStatus.STOPPED} (all servers are off)`);
                return { newStatus: cluster_entity_1.ClusterStatus.STOPPED, isSynced: false };
            }
            return { newStatus: cluster.status, isSynced: true };
        }
        if (runningCount === totalNodes) {
            if (cluster.status !== cluster_entity_1.ClusterStatus.READY) {
                actions.push(`Updated cluster status from ${cluster.status} to ${cluster_entity_1.ClusterStatus.READY} (all servers are running)`);
                return { newStatus: cluster_entity_1.ClusterStatus.READY, isSynced: false };
            }
            return { newStatus: cluster.status, isSynced: true };
        }
        this.logger.warn(`Cluster ${clusterId} has mixed server states: ${runningCount} running, ${stoppedCount} stopped, ${otherCount} other`);
        actions.push(`Detected inconsistent state: ${runningCount} running, ${stoppedCount} stopped, ${otherCount} other`);
        if (autoFix) {
            await this.autoFixMixedState(cluster, runningCount, stoppedCount, actions, clusterId);
            return { newStatus: cluster.status, isSynced: true };
        }
        return { newStatus: cluster.status, isSynced: false };
    }
    async autoFixMixedState(cluster, runningCount, stoppedCount, actions, clusterId) {
        if (cluster.status === cluster_entity_1.ClusterStatus.STOPPED) {
            this.logger.log(`Auto-fix: stopping ${runningCount} running servers to match STOPPED state`);
            await this.stopCluster(clusterId);
            actions.push(`Auto-fixed: stopped ${runningCount} running servers`);
        }
        else if (cluster.status === cluster_entity_1.ClusterStatus.READY) {
            this.logger.log(`Auto-fix: starting ${stoppedCount} stopped servers to match READY state`);
            await this.startCluster(clusterId);
            actions.push(`Auto-fixed: started ${stoppedCount} stopped servers`);
        }
    }
    /**
     * Enrich cluster DTO with real-time status from provider
     */
    async enrichClusterWithRealStatus(cluster) {
        this.logger.log(`Enriching cluster ${cluster.id} with real-time status from provider`);
        try {
            const provider = await this.getProviderWithPowerManagement(cluster);
            let runningCount = 0;
            let stoppedCount = 0;
            // Enrich each node with provider status
            const enrichedNodes = await Promise.all(cluster.nodes.map(async (node) => {
                try {
                    const serverDto = await provider.getServerDetailsAsDto(node.providerResourceId);
                    if (serverDto) {
                        const providerStatus = serverDto.status;
                        const isSynced = (cluster.status === cluster_entity_1.ClusterStatus.READY &&
                            providerStatus === 'running') ||
                            (cluster.status === cluster_entity_1.ClusterStatus.STOPPED &&
                                providerStatus === 'off');
                        if (providerStatus === 'running')
                            runningCount++;
                        if (providerStatus === 'off')
                            stoppedCount++;
                        return {
                            ...node,
                            provider_status: providerStatus,
                            is_synced: isSynced,
                        };
                    }
                    return {
                        ...node,
                        provider_status: 'unknown',
                        is_synced: false,
                    };
                }
                catch (error) {
                    this.logger.warn(`Failed to get status for node ${node.serverName}: ${error.message}`);
                    return {
                        ...node,
                        provider_status: 'unknown',
                        is_synced: false,
                    };
                }
            }));
            // Determine cluster real status
            let clusterRealStatus;
            if (stoppedCount === cluster.nodes.length) {
                clusterRealStatus = 'all_stopped';
            }
            else if (runningCount === cluster.nodes.length) {
                clusterRealStatus = 'all_running';
            }
            else {
                clusterRealStatus = 'mixed';
            }
            const isSynced = (cluster.status === cluster_entity_1.ClusterStatus.READY &&
                clusterRealStatus === 'all_running') ||
                (cluster.status === cluster_entity_1.ClusterStatus.STOPPED &&
                    clusterRealStatus === 'all_stopped');
            // Map to DTO
            return {
                id: cluster.id,
                name: cluster.name,
                provider: cluster.provider,
                region: cluster.region,
                nodeSize: cluster.nodeSize,
                nodeCount: cluster.nodeCount,
                status: cluster.status,
                clusterType: cluster.clusterType,
                autoscalingEnabled: cluster.autoscalingEnabled,
                minNodes: cluster.minNodes,
                maxNodes: cluster.maxNodes,
                k3sVersion: cluster.k3sVersion,
                masterIpAddress: cluster.masterIpAddress,
                nodes: enrichedNodes,
                cluster_real_status: clusterRealStatus,
                is_synced: isSynced,
                createdAt: cluster.createdAt,
                updatedAt: cluster.updatedAt,
            };
        }
        catch (error) {
            this.logger.error(`Failed to enrich cluster with real status: ${error.message}`);
            throw error;
        }
    }
    /**
     * Execute stop cluster operation - called by queue processor
     */
    async executeStopCluster(clusterId, operationId) {
        this.logger.log(`Executing stop cluster for ${clusterId} (operation: ${operationId})`);
        try {
            // Step 0: CLUSTER_STOP_INIT (5%)
            await this.updateOperationStep(operationId, 0, 0, {
                status: infrastructure_operations_entity_1.OperationStatus.IN_PROGRESS,
            });
            // Get cluster with nodes
            const cluster = await this.clusterRepository.findOne({
                where: { id: clusterId },
                relations: ['nodes'],
            });
            if (!cluster) {
                throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
            }
            // Get provider
            const provider = await this.getProviderWithPowerManagement(cluster);
            await this.updateOperationStep(operationId, 0, 100, {
                message: 'Initialization complete',
            });
            // Step 1: CLUSTER_STOP_SERVERS (85%)
            await this.updateOperationStep(operationId, 1, 0, {
                message: 'Stopping cluster servers',
            });
            let stoppedCount = 0;
            const totalNodes = cluster.nodes.length;
            for (let i = 0; i < cluster.nodes.length; i++) {
                const node = cluster.nodes[i];
                this.logger.log(`Stopping server ${i + 1}/${totalNodes}: ${node.serverName}`);
                try {
                    // Validate providerResourceId
                    if (!node.providerResourceId ||
                        Number.isNaN(Number(node.providerResourceId))) {
                        throw new Error(`Node ${node.serverName} has invalid providerResourceId: ${node.providerResourceId}`);
                    }
                    // Check current server status
                    const serverDto = await provider.getServerDetailsAsDto(node.providerResourceId);
                    if (!serverDto) {
                        throw new Error(`Failed to get server details for ${node.serverName} (${node.providerResourceId})`);
                    }
                    if (serverDto.status !== 'off') {
                        this.logger.log(`Powering off server ${node.serverName} (${node.providerResourceId})`);
                        await provider.powerOffServer(node.providerResourceId);
                        stoppedCount++;
                    }
                    else {
                        this.logger.log(`Server ${node.serverName} is already off`);
                    }
                    // Update progress within step
                    const stepProgress = Math.floor(((i + 1) / totalNodes) * 100);
                    await this.updateOperationStep(operationId, 1, stepProgress, {
                        currentNode: `${i + 1}/${totalNodes}`,
                        currentNodeName: node.serverName,
                        serversStopped: stoppedCount,
                    });
                }
                catch (error) {
                    this.logger.error(`Failed to stop server ${node.serverName}: ${error.message}`);
                    throw error;
                }
            }
            // Step 2: CLUSTER_STOP_UPDATE_STATUS (10%)
            await this.updateOperationStep(operationId, 2, 0, {
                message: 'Updating cluster status',
            });
            // Update cluster status
            cluster.status = cluster_entity_1.ClusterStatus.STOPPED;
            await this.clusterRepository.save(cluster);
            await this.updateOperationStep(operationId, 2, 100, {
                status: infrastructure_operations_entity_1.OperationStatus.COMPLETED,
                message: `Cluster stopped successfully. ${stoppedCount}/${totalNodes} servers stopped`,
                serversStopped: stoppedCount,
                totalServers: totalNodes,
                monthlySavingsEstimate: this.calculateSavings(totalNodes),
            });
            this.logger.log(`Successfully stopped ${stoppedCount} servers in cluster ${clusterId}`);
        }
        catch (error) {
            this.logger.error(`Error executing stop cluster: ${error.message}`, error.stack);
            throw error;
        }
    }
    /**
     * Execute start cluster operation - called by queue processor
     */
    async executeStartCluster(clusterId, operationId) {
        this.logger.log(`Executing start cluster for ${clusterId} (operation: ${operationId})`);
        try {
            // Step 0: CLUSTER_START_INIT (5%)
            await this.updateOperationStep(operationId, 0, 0, {
                status: infrastructure_operations_entity_1.OperationStatus.IN_PROGRESS,
            });
            // Get cluster with nodes
            const cluster = await this.clusterRepository.findOne({
                where: { id: clusterId },
                relations: ['nodes'],
            });
            if (!cluster) {
                throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
            }
            // Get provider
            const provider = await this.getProviderWithPowerManagement(cluster);
            await this.updateOperationStep(operationId, 0, 100, {
                message: 'Initialization complete',
            });
            // Step 1: CLUSTER_START_SERVERS (70%)
            await this.updateOperationStep(operationId, 1, 0, {
                message: 'Starting cluster servers',
            });
            let startedCount = 0;
            const totalNodes = cluster.nodes.length;
            for (let i = 0; i < cluster.nodes.length; i++) {
                const node = cluster.nodes[i];
                this.logger.log(`Starting server ${i + 1}/${totalNodes}: ${node.serverName}`);
                try {
                    // Validate providerResourceId
                    if (!node.providerResourceId ||
                        Number.isNaN(Number(node.providerResourceId))) {
                        throw new Error(`Node ${node.serverName} has invalid providerResourceId: ${node.providerResourceId}`);
                    }
                    // Check current server status
                    const serverDto = await provider.getServerDetailsAsDto(node.providerResourceId);
                    if (!serverDto) {
                        throw new Error(`Failed to get server details for ${node.serverName} (${node.providerResourceId})`);
                    }
                    if (serverDto.status === 'off') {
                        this.logger.log(`Powering on server ${node.serverName} (${node.providerResourceId})`);
                        await provider.powerOnServer(node.providerResourceId);
                        startedCount++;
                    }
                    else {
                        this.logger.log(`Server ${node.serverName} is already running`);
                    }
                    // Update progress within step
                    const stepProgress = Math.floor(((i + 1) / totalNodes) * 100);
                    await this.updateOperationStep(operationId, 1, stepProgress, {
                        currentNode: `${i + 1}/${totalNodes}`,
                        currentNodeName: node.serverName,
                        serversStarted: startedCount,
                    });
                }
                catch (error) {
                    this.logger.error(`Failed to start server ${node.serverName}: ${error.message}`);
                    throw error;
                }
            }
            // Step 2: CLUSTER_START_WAIT_READY (15%)
            await this.updateOperationStep(operationId, 2, 0, {
                message: 'Waiting for servers to become ready',
            });
            await this.updateOperationStep(operationId, 2, 100, {
                message: 'Servers are starting up',
            });
            // Step 3: CLUSTER_START_UPDATE_STATUS (10%)
            await this.updateOperationStep(operationId, 3, 0, {
                message: 'Updating cluster status',
            });
            // Update cluster status
            cluster.status = cluster_entity_1.ClusterStatus.READY;
            await this.clusterRepository.save(cluster);
            await this.updateOperationStep(operationId, 3, 100, {
                status: infrastructure_operations_entity_1.OperationStatus.COMPLETED,
                message: `Cluster started successfully. ${startedCount}/${totalNodes} servers started`,
                serversStarted: startedCount,
                totalServers: totalNodes,
            });
            this.logger.log(`Successfully started ${startedCount} servers in cluster ${clusterId}`);
        }
        catch (error) {
            this.logger.error(`Error executing start cluster: ${error.message}`, error.stack);
            throw error;
        }
    }
    /**
     * Update operation step helper
     */
    async updateOperationStep(operationId, stepIndex, stepProgress, metadata = {}) {
        const operation = await this.operationRepository.findOne({
            where: { id: operationId },
        });
        if (!operation) {
            throw new Error(`Operation ${operationId} not found`);
        }
        // Get saved steps from metadata
        const savedSteps = operation.metadata?.operationSteps || [];
        const stepConfig = (0, operation_steps_helper_1.getStepConfigFromSaved)(savedSteps, stepIndex);
        if (!stepConfig) {
            throw new Error(`Invalid step index ${stepIndex} for operation ${operationId}`);
        }
        const overallProgress = (0, operation_steps_helper_1.calculateOperationProgressFromSaved)(savedSteps, stepIndex, stepProgress);
        operation.currentStep = stepConfig.step;
        operation.currentStepIndex = stepIndex;
        operation.totalSteps = savedSteps.length;
        operation.currentStepProgress = stepProgress;
        operation.progress = overallProgress;
        // Update status if provided in metadata
        if (metadata.status) {
            operation.status = metadata.status;
            if (metadata.status === infrastructure_operations_entity_1.OperationStatus.IN_PROGRESS &&
                !operation.startedAt) {
                operation.startedAt = new Date();
            }
            if (metadata.status === infrastructure_operations_entity_1.OperationStatus.COMPLETED) {
                operation.completedAt = new Date();
            }
        }
        operation.metadata = {
            ...operation.metadata,
            ...metadata,
            stepDescription: stepConfig.description,
            stepWeight: stepConfig.weight,
        };
        await this.operationRepository.save(operation);
    }
    /**
     * Get provider and verify it supports power management
     */
    async getProviderWithPowerManagement(cluster) {
        const provider = this.providerFactory.getProvider(cluster.provider);
        // Check if provider supports power management
        if (!provider.powerOnServer || !provider.powerOffServer) {
            throw new common_1.BadRequestException(`Provider ${cluster.provider} does not support power management operations`);
        }
        return provider;
    }
    /**
     * Calculate monthly savings when cluster is stopped
     */
    calculateSavings(nodeCount) {
        const savingsPerNode = 6.9; // ~7.50€ running - ~0.60€ storage = ~6.90€ savings
        const totalSavings = savingsPerNode * nodeCount;
        return `~${totalSavings.toFixed(2)}€`;
    }
};
exports.ClusterPowerManagementService = ClusterPowerManagementService;
exports.ClusterPowerManagementService = ClusterPowerManagementService = ClusterPowerManagementService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(cluster_entity_1.ClusterEntity)),
    __param(1, (0, typeorm_1.InjectRepository)(infrastructure_operations_entity_1.InfrastructureOperationEntity)),
    __param(2, (0, bull_1.InjectQueue)('infrastructure')),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository, Object, provider_factory_1.ProviderFactory])
], ClusterPowerManagementService);
