"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 ClusterOperationsService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClusterOperationsService = 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 encryption_service_1 = require("../../../shared/encryption/services/encryption.service");
const cluster_mapper_service_1 = require("./cluster-mapper.service");
const provider_factory_1 = require("../../../providers/services/provider.factory");
const label_service_1 = require("../../shared/services/label.service");
/**
 * Service for additional cluster operations
 * (kubeconfig, nodes, metadata updates, registration, etc.)
 */
let ClusterOperationsService = ClusterOperationsService_1 = class ClusterOperationsService {
    constructor(clusterRepository, nodeRepository, encryptionService, clusterMapperService, providerFactory, labelService) {
        this.clusterRepository = clusterRepository;
        this.nodeRepository = nodeRepository;
        this.encryptionService = encryptionService;
        this.clusterMapperService = clusterMapperService;
        this.providerFactory = providerFactory;
        this.labelService = labelService;
        this.logger = new common_1.Logger(ClusterOperationsService_1.name);
    }
    /**
     * Register an existing externally-managed cluster
     */
    async registerCluster(dto) {
        this.logger.log(`📝 Registering external cluster: ${dto.name}`);
        this.logger.debug(`📊 Registration Data:`);
        this.logger.debug(`   - Name: ${dto.name}`);
        this.logger.debug(`   - Provider: ${dto.provider}`);
        this.logger.debug(`   - Region: ${dto.region}`);
        this.logger.debug(`   - Master IP: ${dto.masterIpAddress}`);
        this.logger.debug(`   - Cluster Type: ${dto.metadata?.isObservabilityCluster ? 'OBSERVABILITY' : 'WORKLOAD'}`);
        this.logger.debug(`   - K3s Version: ${dto.k3sVersion || 'not provided'}`);
        // Allow re-registration (upsert): check by CLI-provided ID first, then by name
        if (dto.clusterId) {
            const existingById = await this.clusterRepository.findOne({
                where: { id: dto.clusterId },
                relations: ['nodes'],
            });
            if (existingById) {
                this.logger.log(`Cluster with ID '${dto.clusterId}' already exists, deleting and re-creating...`);
                await this.clusterRepository.remove(existingById);
            }
        }
        const existingCluster = await this.clusterRepository.findOne({
            where: { name: dto.name },
            relations: ['nodes'],
        });
        if (existingCluster) {
            this.logger.log(`Cluster '${dto.name}' already exists, deleting and re-creating...`);
            await this.clusterRepository.remove(existingCluster);
        }
        const kubeconfigEncrypted = dto.kubeconfigEncrypted;
        const isObservability = !!dto.metadata?.isObservabilityCluster;
        if (!kubeconfigEncrypted && !isObservability) {
            throw new common_1.BadRequestException('kubeconfigEncrypted is required for cluster registration');
        }
        const clusterType = dto.metadata?.isObservabilityCluster
            ? cluster_entity_1.ClusterType.OBSERVABILITY
            : cluster_entity_1.ClusterType.WORKLOAD;
        const cluster = this.clusterRepository.create({
            ...(dto.clusterId ? { id: dto.clusterId } : {}),
            name: dto.name,
            provider: dto.provider,
            region: dto.region,
            nodeSize: dto.nodeSize,
            nodeCount: 1,
            status: cluster_entity_1.ClusterStatus.READY,
            clusterType,
            k3sTokenEncrypted: dto.k3sTokenEncrypted,
            kubeconfigEncrypted,
            k3sVersion: dto.k3sVersion,
            image: dto.image,
            sshKeyIds: dto.sshKeyIds || [],
            masterIpAddress: dto.masterIpAddress,
            metadata: {
                ...dto.metadata,
                externallyManaged: true,
                registeredAt: new Date().toISOString(),
                masterIpAddress: dto.masterIpAddress,
            },
        });
        const savedCluster = await this.clusterRepository.save(cluster);
        this.logger.debug(`✅ Cluster saved to database:`);
        this.logger.debug(`   - Cluster ID: ${savedCluster.id}`);
        this.logger.debug(`   - Cluster Type: ${savedCluster.clusterType}`);
        this.logger.debug(`   - Status: ${savedCluster.status}`);
        this.logger.debug(`   - masterIpAddress (direct field): ${savedCluster.masterIpAddress || 'NOT SET'}`);
        this.logger.debug(`   - metadata.masterIpAddress: ${savedCluster.metadata?.masterIpAddress || 'NOT SET'}`);
        // Create master node record (use .create() + .save() so @BeforeInsert() fires)
        const nodeEntity = this.nodeRepository.create({
            ...(dto.nodeId ? { id: dto.nodeId } : {}),
            clusterId: savedCluster.id,
            serverName: `${dto.name}-master`,
            nodeType: 'master',
            status: 'ready',
            ipAddress: dto.masterIpAddress,
            providerResourceId: dto.masterProviderResourceId || '',
            metadata: {
                externallyManaged: true,
            },
        });
        const savedNode = await this.nodeRepository.save(nodeEntity);
        this.logger.log(`✅ External cluster registered: ${savedCluster.id} (${savedCluster.clusterType})`);
        this.logger.log(`   Master node: ${savedNode.serverName} (${dto.masterIpAddress})`);
        if (savedNode.providerResourceId && dto.provider) {
            await this.tagClusterServer(savedCluster, savedNode, dto.provider);
        }
        else {
            this.logger.warn(`Skipping server tagging: providerResourceId or provider not available`);
        }
        return {
            cluster_id: savedCluster.id,
            node_id: savedNode.id,
            metrics_endpoint: `/api/v1/servers/${savedNode.id}/metrics`,
            status: 'registered',
        };
    }
    async getKubeconfig(clusterId) {
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        if (!cluster.kubeconfigEncrypted) {
            throw new common_1.NotFoundException(`Kubeconfig not available for cluster ${clusterId}`);
        }
        const kubeconfig = this.encryptionService.decrypt(cluster.kubeconfigEncrypted);
        const override = process.env.KUBECONFIG_SERVER_OVERRIDE;
        if (override) {
            return kubeconfig.replaceAll(/server:\s*https?:\/\/[^\s]+/g, `server: ${override}`);
        }
        return kubeconfig;
    }
    async getClusterNodes(clusterId) {
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
            relations: ['nodes'],
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        return cluster.nodes;
    }
    async updateClusterMetadata(clusterId, metadata) {
        const cluster = await this.clusterRepository.findOne({
            where: { id: clusterId },
        });
        if (!cluster) {
            throw new common_1.NotFoundException(`Cluster ${clusterId} not found`);
        }
        cluster.metadata = { ...cluster.metadata, ...metadata };
        const updated = await this.clusterRepository.save(cluster);
        return this.clusterMapperService.mapToDto(updated);
    }
    async updateNodeMetadata(clusterId, nodeId, metadata) {
        const node = await this.nodeRepository.findOne({
            where: { id: nodeId, clusterId },
        });
        if (!node) {
            throw new common_1.NotFoundException(`Node ${nodeId} not found in cluster ${clusterId}`);
        }
        node.metadata = { ...node.metadata, ...metadata };
        return await this.nodeRepository.save(node);
    }
    async reconcileClusterTags(clusterId, force, includeFirewalls) {
        return {
            clusterId,
            message: 'Tag reconciliation not yet implemented in new architecture',
            force,
            includeFirewalls,
        };
    }
    async reconcileClusterFirewalls(clusterId, options) {
        return {
            clusterId,
            message: 'Firewall reconciliation uses new desired-state API. Use PUT /firewalls/:id/desired-rules and POST /firewalls/:id/reconcile instead.',
            ...options,
        };
    }
    async tagClusterServer(cluster, node, provider) {
        try {
            const providerService = this.providerFactory.getProvider(provider);
            if (!providerService.updateServerLabels) {
                this.logger.warn(`Provider '${provider}' does not support label updates`);
                return;
            }
            const serverDetails = await providerService.getServerDetailsAsDto(node.providerResourceId);
            if (!serverDetails) {
                this.logger.warn(`Server ${node.providerResourceId} not found in provider`);
                return;
            }
            const existingLabels = serverDetails.labels
                ? this.labelService.toRecord(serverDetails.labels)
                : {};
            const clusterLabels = this.labelService.generateServerLabels({
                resourceType: 'cluster-node',
                clusterId: cluster.id,
                clusterName: cluster.name,
                nodeId: node.id,
                nodeType: node.nodeType,
            });
            const mergedLabels = {
                ...existingLabels,
                ...this.labelService.toRecord(clusterLabels),
            };
            await providerService.updateServerLabels(node.providerResourceId, mergedLabels);
        }
        catch (error) {
            this.logger.error(`Failed to tag server ${node.providerResourceId}: ${error.message}`, error.stack);
        }
    }
};
exports.ClusterOperationsService = ClusterOperationsService;
exports.ClusterOperationsService = ClusterOperationsService = ClusterOperationsService_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,
        encryption_service_1.EncryptionService,
        cluster_mapper_service_1.ClusterMapperService,
        provider_factory_1.ProviderFactory,
        label_service_1.LabelService])
], ClusterOperationsService);
