"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 VnetProvisioningService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.VnetProvisioningService = void 0;
const common_1 = require("@nestjs/common");
const cloud_provider_enum_1 = require("../../../../src/modules/providers/enums/cloud-provider.enum");
const provider_factory_1 = require("../../../../src/modules/providers/core/factories/provider.factory");
const vnet_subnet_entity_1 = require("../../../../src/modules/infrastructure/vnets/entities/vnet-subnet.entity");
const vnet_entity_1 = require("../../../../src/modules/infrastructure/vnets/entities/vnet.entity");
const cli_vnet_repository_1 = require("../repositories/cli-vnet.repository");
const HETZNER_DEFAULT_NETWORK_ZONE_BY_REGION = {
    nbg1: 'eu-central',
    fsn1: 'eu-central',
    hel1: 'eu-central',
    ash: 'us-east',
    hil: 'us-west',
};
let VnetProvisioningService = VnetProvisioningService_1 = class VnetProvisioningService {
    constructor(providerFactory, vnetRepo) {
        this.providerFactory = providerFactory;
        this.vnetRepo = vnetRepo;
        this.logger = new common_1.Logger(VnetProvisioningService_1.name);
    }
    async ensureEnvVnet(spec) {
        const existing = await this.vnetRepo.findActive();
        if (existing?.provider === spec.provider) {
            const subnet = existing.subnets?.[0];
            if (!subnet) {
                throw new Error(`VNet ${existing.providerResourceId} has no subnet — recreate the environment`);
            }
            this.logger.log(`Reusing existing VNet ${existing.providerResourceId} (subnet ${subnet.providerSubnetId})`);
            return this.toInfo(existing, subnet);
        }
        const ipRange = spec.ipRange || '10.10.0.0/16';
        const subnetIpRange = spec.subnetIpRange || '10.10.1.0/24';
        if (spec.provider === cloud_provider_enum_1.CloudProvider.HETZNER) {
            return this.createHetznerVnet(spec, ipRange, subnetIpRange);
        }
        if (spec.provider === cloud_provider_enum_1.CloudProvider.SCALEWAY) {
            return this.createScalewayVnet(spec, ipRange, subnetIpRange);
        }
        throw new Error(`VNet provisioning not yet implemented for provider ${spec.provider}`);
    }
    async createHetznerVnet(spec, ipRange, subnetIpRange) {
        const networkZone = spec.networkZone || 'eu-central';
        this.logger.log(`Creating Hetzner VNet ${spec.name} (${ipRange})`);
        const provider = this.providerFactory.getProvider(cloud_provider_enum_1.CloudProvider.HETZNER);
        if (!provider.createVNet) {
            throw new Error('Hetzner provider does not implement createVNet');
        }
        const created = await provider.createVNet({
            name: spec.name,
            ipRange,
            labels: [
                { key: 'managed-by', value: 'flui-cloud' },
                { key: 'flui-resource-type', value: 'vnet' },
            ],
            subnets: [{ ipRange: subnetIpRange, networkZone }],
        });
        const vnetRecord = await this.vnetRepo.save({
            providerResourceId: created.vnetId,
            name: spec.name,
            provider: spec.provider,
            ipRange: created.ipRange,
            labels: [
                { key: 'managed-by', value: 'flui-cloud' },
                { key: 'flui-resource-type', value: 'vnet' },
            ],
            status: vnet_entity_1.VNetStatus.ACTIVE,
            subnets: [],
        });
        const subnetRecord = await this.vnetRepo.addSubnet(vnetRecord.id, {
            providerSubnetId: subnetIpRange,
            ipRange: subnetIpRange,
            type: vnet_subnet_entity_1.SubnetType.CLOUD,
            networkZone,
        });
        return this.toInfo({ ...vnetRecord, subnets: [subnetRecord] }, subnetRecord);
    }
    async createScalewayVnet(spec, ipRange, subnetIpRange) {
        const region = spec.region || 'fr-par';
        this.logger.log(`Creating Scaleway Private Network ${spec.name} in ${region} (${ipRange})`);
        const provider = this.providerFactory.getProvider(cloud_provider_enum_1.CloudProvider.SCALEWAY);
        if (!provider.createVNet) {
            throw new Error('Scaleway provider does not implement createVNet');
        }
        const created = await provider.createVNet({
            name: spec.name,
            ipRange,
            labels: [
                { key: 'managed-by', value: 'flui-cloud' },
                { key: 'flui-resource-type', value: 'vnet' },
                { key: 'region', value: region },
            ],
            subnets: [{ ipRange: subnetIpRange, networkZone: region }],
        });
        const vnetRecord = await this.vnetRepo.save({
            providerResourceId: created.vnetId,
            name: spec.name,
            provider: spec.provider,
            ipRange: created.ipRange,
            labels: [
                { key: 'managed-by', value: 'flui-cloud' },
                { key: 'flui-resource-type', value: 'vnet' },
                { key: 'region', value: region },
            ],
            status: vnet_entity_1.VNetStatus.ACTIVE,
            subnets: [],
        });
        const subnetRecord = await this.vnetRepo.addSubnet(vnetRecord.id, {
            providerSubnetId: subnetIpRange,
            ipRange: subnetIpRange,
            type: vnet_subnet_entity_1.SubnetType.CLOUD,
            networkZone: region,
        });
        return this.toInfo({ ...vnetRecord, subnets: [subnetRecord] }, subnetRecord);
    }
    async destroyEnvVnet() {
        const existing = await this.vnetRepo.findActive();
        if (!existing)
            return;
        try {
            const provider = this.providerFactory.getProvider(existing.provider);
            if (provider.deleteVNet) {
                await provider.deleteVNet(existing.providerResourceId);
            }
        }
        catch (error) {
            this.logger.warn(`Failed to delete VNet ${existing.providerResourceId} on ${existing.provider}: ${error.message}`);
        }
        await this.vnetRepo.remove(existing.id);
    }
    hetznerNetworkZoneFor(region) {
        return HETZNER_DEFAULT_NETWORK_ZONE_BY_REGION[region] || 'eu-central';
    }
    toInfo(vnet, subnet) {
        return {
            vnetId: vnet.id,
            vnetProviderResourceId: vnet.providerResourceId,
            vnetName: vnet.name,
            vnetIpRange: vnet.ipRange,
            subnetId: subnet.id,
            subnetProviderResourceId: subnet.providerSubnetId || subnet.ipRange,
            subnetIpRange: subnet.ipRange,
            subnetType: subnet.type,
            networkZone: subnet.networkZone,
        };
    }
};
exports.VnetProvisioningService = VnetProvisioningService;
exports.VnetProvisioningService = VnetProvisioningService = VnetProvisioningService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [provider_factory_1.ProviderFactory,
        cli_vnet_repository_1.CliVnetRepository])
], VnetProvisioningService);
