"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 ManagementService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManagementService = void 0;
const common_1 = require("@nestjs/common");
const provider_definitions_service_1 = require("./provider-definitions.service");
const configuration_mode_service_1 = require("./configuration-mode.service");
const provider_configuration_repository_1 = require("../repositories/provider-configuration.repository");
const access_service_1 = require("../../access/services/access.service");
const provider_status_enum_1 = require("../entities/provider-status.enum");
const credentials_entity_1 = require("../entities/credentials.entity");
const provider_factory_1 = require("../../../modules/providers/services/provider.factory");
const capabilities_provider_factory_1 = require("../../../modules/providers/core/factories/capabilities-provider.factory");
const cache_service_1 = require("../../../modules/common/cache/cache.service");
const cache_category_enum_1 = require("../../../modules/common/cache/enums/cache-category.enum");
let ManagementService = ManagementService_1 = class ManagementService {
    constructor(providerDefinitions, configurationMode, providerConfigRepo, accessService, providerFactory, capabilitiesFactory, cacheService) {
        this.providerDefinitions = providerDefinitions;
        this.configurationMode = configurationMode;
        this.providerConfigRepo = providerConfigRepo;
        this.accessService = accessService;
        this.providerFactory = providerFactory;
        this.capabilitiesFactory = capabilitiesFactory;
        this.cacheService = cacheService;
        this.logger = new common_1.Logger(ManagementService_1.name);
    }
    async getAvailableProviders() {
        return this.providerDefinitions.getAllProviders();
    }
    async getProvider(provider) {
        const providerDef = await this.providerDefinitions.getProvider(provider);
        if (!providerDef) {
            throw new common_1.NotFoundException(`Provider ${provider} not found`);
        }
        return providerDef;
    }
    getProviderLogo(provider) {
        return this.providerDefinitions.getProviderLogo(provider);
    }
    async getUserProviderConfigurations(filters) {
        let configurations;
        if (filters?.provider) {
            const config = await this.providerConfigRepo.findByProvider(filters.provider);
            configurations = config ? [config] : [];
        }
        else if (filters?.status) {
            configurations = await this.providerConfigRepo.findByStatus(filters.status);
        }
        else if (filters?.isActive !== undefined) {
            if (filters.isActive) {
                configurations = await this.providerConfigRepo.findActiveProviders();
            }
            else {
                const allConfigs = await this.providerConfigRepo.findAll();
                configurations = allConfigs.filter((config) => !config.isActive);
            }
        }
        else {
            configurations = await this.providerConfigRepo.findAll();
        }
        return this.enrichConfigurationsWithProviderData(configurations);
    }
    async getProviderConfiguration(provider) {
        const config = await this.providerConfigRepo.findByProvider(provider);
        if (!config) {
            throw new common_1.NotFoundException(`Configuration for provider ${provider} not found`);
        }
        const enriched = await this.enrichConfigurationsWithProviderData([config]);
        return enriched[0];
    }
    async configureProvider(configDto) {
        if (this.configurationMode.isHostedMode()) {
            throw new common_1.BadRequestException('Provider configuration not available in hosted mode');
        }
        await this.getProvider(configDto.provider);
        const existingConfig = await this.providerConfigRepo.findByProvider(configDto.provider);
        if (existingConfig) {
            throw new common_1.BadRequestException(`Provider ${configDto.provider} is already configured`);
        }
        const newConfig = await this.providerConfigRepo.create({
            provider: configDto.provider,
            status: provider_status_enum_1.ProviderStatus.CONFIGURING,
            enabledRegions: configDto.enabledRegions,
            configuration: configDto.additionalConfig || {},
            isActive: false,
            metadata: {},
        });
        try {
            await this.storeProviderCredentials(configDto);
            await this.providerConfigRepo.update(newConfig.id, {
                status: provider_status_enum_1.ProviderStatus.VALIDATING,
            });
            const validationResult = await this.validateProviderCredentials(configDto.provider, configDto.credentials);
            if (validationResult.success) {
                await this.providerConfigRepo.update(newConfig.id, {
                    status: provider_status_enum_1.ProviderStatus.ACTIVE,
                    isActive: true,
                    lastHealthCheck: new Date(),
                    metadata: {
                        lastValidation: new Date(),
                        regionsDiscovered: validationResult.availableRegions?.length || 0,
                    },
                });
            }
            else {
                await this.providerConfigRepo.update(newConfig.id, {
                    status: provider_status_enum_1.ProviderStatus.ERROR,
                    metadata: {
                        errorMessage: validationResult.message,
                        lastValidation: new Date(),
                    },
                });
            }
            const updatedConfig = await this.providerConfigRepo.findByProvider(configDto.provider);
            const enriched = await this.enrichConfigurationsWithProviderData([
                updatedConfig,
            ]);
            return enriched[0];
        }
        catch (error) {
            await this.providerConfigRepo.update(newConfig.id, {
                status: provider_status_enum_1.ProviderStatus.ERROR,
                metadata: {
                    errorMessage: error.message,
                },
            });
            throw error;
        }
    }
    async validateProvider(provider, credentials) {
        const providerDef = await this.providerDefinitions.getProvider(provider);
        if (!providerDef) {
            throw new common_1.NotFoundException(`Provider ${provider} not found`);
        }
        return this.validateProviderCredentials(provider, credentials);
    }
    async enableProvider(provider, enabled) {
        const config = await this.providerConfigRepo.findByProvider(provider);
        if (!config) {
            throw new common_1.NotFoundException(`Provider ${provider} not configured`);
        }
        const updatedConfig = await this.providerConfigRepo.update(config.id, {
            isActive: enabled,
            status: enabled ? provider_status_enum_1.ProviderStatus.ACTIVE : provider_status_enum_1.ProviderStatus.DISABLED,
        });
        const enriched = await this.enrichConfigurationsWithProviderData([
            updatedConfig,
        ]);
        return enriched[0];
    }
    async getProviderHealth(provider) {
        const config = await this.providerConfigRepo.findByProvider(provider);
        if (!config) {
            throw new common_1.NotFoundException(`Provider ${provider} not configured`);
        }
        const startTime = Date.now();
        try {
            const providerService = this.providerFactory.getProvider(provider);
            const validation = await providerService.testConnection();
            const responseTime = Date.now() - startTime;
            const healthStatus = {
                providerId: provider,
                status: validation.success ? 'healthy' : 'unhealthy',
                responseTime,
                lastCheck: new Date(),
                errorMessage: validation.success ? undefined : validation.error,
                metrics: {
                    apiCallsToday: 0,
                    errorRate: validation.success ? 0 : 1,
                },
            };
            await this.providerConfigRepo.updateHealthCheck(config.id, {
                lastHealthCheck: new Date(),
                metadata: {
                    ...config.metadata,
                    lastHealthStatus: healthStatus.status,
                    lastResponseTime: responseTime,
                },
            });
            return healthStatus;
        }
        catch (error) {
            const responseTime = Date.now() - startTime;
            return {
                providerId: provider,
                status: 'unhealthy',
                responseTime,
                lastCheck: new Date(),
                errorMessage: error.message,
                metrics: {
                    apiCallsToday: 0,
                    errorRate: 1,
                },
            };
        }
    }
    async getProviderRegions(provider) {
        return this.providerDefinitions.getProviderRegions(provider);
    }
    async getProviderInstanceTypes(provider) {
        return this.providerDefinitions.getProviderInstanceTypes(provider);
    }
    async getConfigurationMode() {
        return {
            mode: this.configurationMode.getCurrentMode(),
            capabilities: this.configurationMode.getCapabilities(),
        };
    }
    async enrichConfigurationsWithProviderData(configurations) {
        return Promise.all(configurations.map(async (config) => {
            const providerDef = await this.providerDefinitions.getProvider(config.provider);
            const credMetadata = await this.accessService.getActiveCredentialMetadata(config.provider);
            return {
                id: config.id,
                provider: config.provider,
                status: config.status,
                enabledRegions: config.enabledRegions,
                lastHealthCheck: config.lastHealthCheck,
                isActive: config.isActive,
                availableRegions: providerDef?.capabilities.supportedRegions,
                credentialsType: credMetadata?.credentialType,
                credentialsExpiresAt: credMetadata?.expiresAt ?? null,
                metadata: {
                    ...config.metadata,
                    apiKeyMasked: await this.getMaskedCredentials(config.provider),
                },
                createdAt: config.createdAt,
                updatedAt: config.updatedAt,
            };
        }));
    }
    async rotateProviderCredentials(provider, credentials) {
        if (this.configurationMode.isHostedMode()) {
            throw new common_1.BadRequestException('Provider configuration not available in hosted mode');
        }
        const config = await this.providerConfigRepo.findByProvider(provider);
        if (!config) {
            throw new common_1.NotFoundException(`Provider ${provider} not configured`);
        }
        if (credentials.provider !== provider) {
            throw new common_1.BadRequestException('Credentials provider does not match URL provider');
        }
        const validation = await this.validateProviderCredentials(provider, credentials);
        if (!validation.success) {
            throw new common_1.BadRequestException(validation.message || 'Credential validation failed');
        }
        await this.accessService.deleteActiveProviderCredentials(provider);
        await this.storeProviderCredentials({
            provider,
            credentials,
            enabledRegions: config.enabledRegions,
        });
        const updated = await this.providerConfigRepo.update(config.id, {
            status: provider_status_enum_1.ProviderStatus.ACTIVE,
            isActive: true,
            lastHealthCheck: new Date(),
            metadata: {
                ...config.metadata,
                lastValidation: new Date(),
                errorMessage: undefined,
            },
        });
        const enriched = await this.enrichConfigurationsWithProviderData([updated]);
        return enriched[0];
    }
    async updateProviderCredentialsExpiry(provider, expiresAt) {
        const config = await this.providerConfigRepo.findByProvider(provider);
        if (!config) {
            throw new common_1.NotFoundException(`Provider ${provider} not configured`);
        }
        await this.accessService.updateActiveCredentialExpiry(provider, expiresAt);
        const enriched = await this.enrichConfigurationsWithProviderData([config]);
        return enriched[0];
    }
    async updateProviderRegions(provider, enabledRegions) {
        const config = await this.providerConfigRepo.findByProvider(provider);
        if (!config) {
            throw new common_1.NotFoundException(`Provider ${provider} not configured`);
        }
        const providerDef = await this.getProvider(provider);
        const supported = providerDef.capabilities.supportedRegions ?? [];
        if (supported.length > 0) {
            const supportedSet = new Set(supported.map((r) => r.id));
            const invalid = enabledRegions.filter((r) => !supportedSet.has(r));
            if (invalid.length > 0) {
                throw new common_1.BadRequestException(`Unsupported regions for ${provider}: ${invalid.join(', ')}`);
            }
        }
        const updated = await this.providerConfigRepo.update(config.id, {
            enabledRegions,
        });
        const enriched = await this.enrichConfigurationsWithProviderData([updated]);
        return enriched[0];
    }
    async storeProviderCredentials(configDto) {
        const providerDef = await this.providerDefinitions.getProvider(configDto.provider);
        if (!providerDef) {
            throw new common_1.BadRequestException(`Unsupported provider: ${configDto.provider}`);
        }
        const credType = configDto.credentials.type;
        switch (credType) {
            case credentials_entity_1.CredentialType.API_KEY:
                if (!configDto.credentials.apiKey) {
                    throw new common_1.BadRequestException('API key is required for this provider');
                }
                await this.accessService.createApiToken({
                    provider: configDto.provider,
                    token: configDto.credentials.apiKey,
                    label: `${providerDef.displayName} API Token`,
                    notes: 'Auto-generated during provider configuration',
                    expiresAt: configDto.credentials.expiresAt
                        ? new Date(configDto.credentials.expiresAt)
                        : undefined,
                });
                break;
            case credentials_entity_1.CredentialType.ACCESS_KEY_SECRET:
                if (!configDto.credentials.accessKey ||
                    !configDto.credentials.secretKey) {
                    throw new common_1.BadRequestException('Access Key ID and Secret Key are required for this provider');
                }
                await this.accessService.createAccessKeyPair({
                    provider: configDto.provider,
                    accessKey: configDto.credentials.accessKey,
                    secretKey: configDto.credentials.secretKey,
                    label: `${providerDef.displayName} API Keys`,
                    notes: 'Auto-generated during provider configuration',
                    expiresAt: configDto.credentials.expiresAt
                        ? new Date(configDto.credentials.expiresAt)
                        : undefined,
                });
                break;
            case credentials_entity_1.CredentialType.USER_PASSWORD:
                if (!configDto.credentials.username ||
                    !configDto.credentials.password ||
                    !configDto.credentials.clientId ||
                    !configDto.credentials.clientSecret) {
                    throw new common_1.BadRequestException('Username, password, client ID and client secret are required for this provider');
                }
                await this.accessService.generateBearerToken(configDto.provider, {
                    provider: configDto.provider,
                    username: configDto.credentials.username,
                    password: configDto.credentials.password,
                    client_id: configDto.credentials.clientId,
                    client_secret: configDto.credentials.clientSecret,
                    save_credentials: true,
                });
                break;
            default:
                throw new common_1.BadRequestException(`Unsupported credential type: ${credType}`);
        }
    }
    async validateProviderCredentials(provider, credentials) {
        try {
            // USER_PASSWORD is handled locally (OAuth token exchange, no capabilities service needed)
            if (credentials.type === credentials_entity_1.CredentialType.USER_PASSWORD) {
                if (!credentials.username ||
                    !credentials.password ||
                    !credentials.clientId ||
                    !credentials.clientSecret) {
                    return {
                        success: false,
                        message: 'Username, password, client ID and client secret are required',
                    };
                }
                const request = {
                    provider,
                    client_id: credentials.clientId,
                    client_secret: credentials.clientSecret,
                    username: credentials.username,
                    password: credentials.password,
                    save_credentials: false,
                };
                try {
                    await this.accessService.generateBearerToken(provider, request);
                    return { success: true, message: 'Credentials are valid' };
                }
                catch {
                    return { success: false, message: 'Invalid credentials' };
                }
            }
            // All other types are delegated to the provider's capabilities service
            const capabilitiesService = this.capabilitiesFactory.getCapabilitiesService(provider);
            return capabilitiesService.validateCredentials({
                provider,
                type: credentials.type,
                apiKey: credentials.apiKey,
                accessKey: credentials.accessKey,
                secretKey: credentials.secretKey,
                expiresAt: credentials.expiresAt
                    ? new Date(credentials.expiresAt)
                    : undefined,
                bearerToken: credentials.bearerToken,
            });
        }
        catch (error) {
            return {
                success: false,
                message: error.message || 'Validation failed',
            };
        }
    }
    async getMaskedCredentials(provider) {
        try {
            const providerDef = await this.providerDefinitions.getProvider(provider);
            if (!providerDef)
                return undefined;
            switch (providerDef.capabilities.credentialType) {
                case 'api_key': {
                    const token = await this.accessService.getActiveApiToken(provider);
                    return `***${token.slice(-4)}`;
                }
                case 'access_key_secret': {
                    const pair = await this.accessService.getActiveAccessKeyPair(provider);
                    return `${pair.accessKey.slice(0, 4)}***`;
                }
                case 'user_password': {
                    const bearerToken = await this.accessService.getActiveBearerToken(provider);
                    return `***${bearerToken.access_token.slice(-4)}`;
                }
                default:
                    return undefined;
            }
        }
        catch (error) {
            return undefined;
        }
    }
    async removeProviderConfiguration(configId) {
        const config = await this.providerConfigRepo.findById(configId);
        if (!config) {
            throw new common_1.NotFoundException('Provider configuration not found');
        }
        await this.providerConfigRepo.delete(configId);
    }
    /**
     * Get available node sizes (server types) for a provider
     * Uses partial caching strategy:
     * - Server type metadata (CPU, RAM, prices, etc.) is cached for 24 hours
     * - Real-time availability is ALWAYS fetched fresh (not cached)
     * Optionally filtered by region to show only available server types in that location
     */
    async getNodeSizes(provider, region, skipCache) {
        this.logger.log(`Getting node sizes for provider: ${provider}${region ? ` in region: ${region}` : ''}`);
        const cloudProvider = this.providerFactory.getProvider(provider);
        if (!cloudProvider.getNodeSizes) {
            throw new common_1.BadRequestException(`Node sizes not supported for provider: ${provider}`);
        }
        const cacheKey = this.cacheService.buildKey('provider', provider, 'node-sizes-metadata');
        // Metadata (CPU, RAM, prices) is cached; real-time availability is always fresh
        const metadataNodeSizes = await this.cacheService.wrap(cacheKey, async () => {
            const nodeSizes = await cloudProvider.getNodeSizes(false);
            return nodeSizes;
        }, {
            category: cache_category_enum_1.CacheCategory.CONFIGURATION,
            skipCache,
            shouldCache: cache_service_1.CacheService.shouldCacheResponse,
        });
        const nodeSizesWithAvailability = await cloudProvider.getNodeSizes(true);
        const allNodeSizes = metadataNodeSizes.map((metadata) => {
            const withAvailability = nodeSizesWithAvailability.find((ns) => ns.id === metadata.id);
            return {
                ...metadata,
                availability: withAvailability?.availability || [],
            };
        });
        if (!region) {
            return allNodeSizes;
        }
        const filteredNodeSizes = allNodeSizes.filter((nodeSize) => {
            if (!nodeSize.availability || !Array.isArray(nodeSize.availability)) {
                if (!nodeSize.locations || !Array.isArray(nodeSize.locations)) {
                    return false;
                }
                const location = nodeSize.locations.find((loc) => loc.name === region);
                if (!location)
                    return false;
                if (!location.deprecation)
                    return true;
                return new Date() < new Date(location.deprecation.unavailable_after);
            }
            const locationAvailability = nodeSize.availability.find((av) => av.location === region);
            if (!locationAvailability)
                return false;
            if (locationAvailability.deprecated) {
                const location = nodeSize.locations?.find((loc) => loc.name === region);
                if (location?.deprecation) {
                    if (new Date() >= new Date(location.deprecation.unavailable_after)) {
                        return false;
                    }
                }
            }
            return locationAvailability.available;
        });
        this.logger.log(`Filtered ${allNodeSizes.length} node sizes to ${filteredNodeSizes.length} available in region ${region}`);
        return filteredNodeSizes;
    }
    /**
     * Get pricing information for a provider
     * Cached for 24 hours (CONFIGURATION category)
     */
    async getPricing(provider, query, skipCache) {
        this.logger.log(`Getting pricing for provider: ${provider}`, { query });
        const cacheKey = this.cacheService.buildKey('provider', provider, 'pricing', query.region || 'all', query.nodeSize || 'all');
        return this.cacheService.wrap(cacheKey, async () => {
            const cloudProvider = this.providerFactory.getProvider(provider);
            if (!cloudProvider.getPricing) {
                throw new common_1.BadRequestException(`Pricing not supported for provider: ${provider}`);
            }
            const pricing = await cloudProvider.getPricing(query);
            // Map from provider DTO to management DTO (they're currently identical)
            return pricing;
        }, {
            category: cache_category_enum_1.CacheCategory.CONFIGURATION,
            skipCache,
            // Use helper to validate response should be cached
            shouldCache: cache_service_1.CacheService.shouldCacheResponse,
        });
    }
    /**
     * Clear node sizes cache for a specific provider
     */
    async clearNodeSizesCache(provider) {
        const cacheKey = this.cacheService.buildKey(cache_category_enum_1.CacheCategory.CONFIGURATION, `provider:${provider}:node-sizes`);
        await this.cacheService.delete(cacheKey);
    }
};
exports.ManagementService = ManagementService;
exports.ManagementService = ManagementService = ManagementService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [provider_definitions_service_1.ProviderDefinitionsService,
        configuration_mode_service_1.ConfigurationModeService,
        provider_configuration_repository_1.ProviderConfigurationRepository,
        access_service_1.AccessService,
        provider_factory_1.ProviderFactory,
        capabilities_provider_factory_1.CapabilitiesProviderFactory,
        cache_service_1.CacheService])
], ManagementService);
