"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IntegrationConfigService = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const crypto_1 = require("crypto");
const prisma_service_1 = require("../database/prisma.service");
const integration_config_types_1 = require("./integration-config.types");
let IntegrationConfigService = class IntegrationConfigService {
    prisma;
    config;
    cache = new Map();
    constructor(prisma, config) {
        this.prisma = prisma;
        this.config = config;
    }
    async onModuleInit() {
        await this.refreshCache();
    }
    async refreshCache() {
        const configs = await this.prisma.integrationConfig.findMany();
        this.cache.clear();
        configs.forEach((config) => {
            this.cache.set(config.provider, {
                category: config.category,
                provider: config.provider,
                displayName: config.displayName,
                enabled: config.enabled,
                publicConfig: this.jsonObject(config.publicConfig),
                secretConfig: this.stringMap(config.secretConfig),
                updatedById: config.updatedById,
                updatedAt: config.updatedAt
            });
        });
    }
    listTemplates() {
        return integration_config_types_1.INTEGRATION_TEMPLATES;
    }
    listConfigs() {
        const providers = new Set([...integration_config_types_1.INTEGRATION_TEMPLATES.map((template) => template.provider), ...this.cache.keys()]);
        return Array.from(providers)
            .sort()
            .map((provider) => this.describeProvider(provider));
    }
    getConfig(provider) {
        return this.describeProvider((0, integration_config_types_1.normalizeIntegrationProvider)(provider));
    }
    async updateConfig(provider, input, actorId) {
        const normalizedProvider = (0, integration_config_types_1.normalizeIntegrationProvider)(provider);
        const template = (0, integration_config_types_1.integrationTemplate)(normalizedProvider);
        const existing = this.cache.get(normalizedProvider);
        const category = this.normalizeCategory(input.category ?? existing?.category ?? template?.category);
        if (!category) {
            throw new common_1.BadRequestException('category is required for custom integration providers.');
        }
        const publicConfig = {
            ...(existing?.publicConfig ?? {}),
            ...this.cleanPublicConfig(input.publicConfig)
        };
        for (const key of input.clearPublicKeys ?? []) {
            delete publicConfig[key];
        }
        const secretConfig = {
            ...(existing?.secretConfig ?? {}),
            ...this.encryptSecretConfig(input.secretConfig)
        };
        for (const key of input.clearSecretKeys ?? []) {
            delete secretConfig[key];
        }
        const saved = await this.prisma.integrationConfig.upsert({
            where: { provider: normalizedProvider },
            create: {
                provider: normalizedProvider,
                category,
                displayName: input.displayName ?? template?.displayName ?? normalizedProvider,
                enabled: input.enabled ?? true,
                publicConfig: this.toJson(publicConfig),
                secretConfig: this.toJson(secretConfig),
                updatedById: actorId
            },
            update: {
                category,
                displayName: input.displayName ?? existing?.displayName ?? template?.displayName ?? normalizedProvider,
                enabled: input.enabled,
                publicConfig: this.toJson(publicConfig),
                secretConfig: this.toJson(secretConfig),
                updatedById: actorId
            }
        });
        this.cache.set(saved.provider, {
            category: saved.category,
            provider: saved.provider,
            displayName: saved.displayName,
            enabled: saved.enabled,
            publicConfig: this.jsonObject(saved.publicConfig),
            secretConfig: this.stringMap(saved.secretConfig),
            updatedById: saved.updatedById,
            updatedAt: saved.updatedAt
        });
        return this.describeProvider(saved.provider);
    }
    getValue(provider, key, envKey, fallback = '') {
        return this.cachedPublicValue(provider, key) ?? this.cachedSecretValue(provider, key) ?? (envKey ? this.config.get(envKey) : undefined) ?? fallback;
    }
    getPublicValue(provider, key, envKey) {
        const value = this.cachedPublicValue(provider, key);
        if (value !== undefined && value !== null && value !== '') {
            return value;
        }
        return envKey ? this.config.get(envKey) : undefined;
    }
    getSecretValue(provider, key, envKey) {
        const value = this.cachedSecretValue(provider, key);
        if (value !== undefined && value !== null && value !== '') {
            return value;
        }
        return envKey ? this.config.get(envKey) : undefined;
    }
    isEnabled(provider) {
        return this.cache.get((0, integration_config_types_1.normalizeIntegrationProvider)(provider))?.enabled ?? true;
    }
    hasUsableValue(value) {
        return Boolean(value && !/REPLACE_ME|YOUR_|your-|placeholder/i.test(value));
    }
    describeProvider(provider) {
        const normalizedProvider = (0, integration_config_types_1.normalizeIntegrationProvider)(provider);
        const template = (0, integration_config_types_1.integrationTemplate)(normalizedProvider);
        const stored = this.cache.get(normalizedProvider);
        const publicConfig = stored?.publicConfig ?? {};
        const secretConfig = stored?.secretConfig ?? {};
        const secretKeys = Array.from(new Set([...(template?.secretKeys ?? []), ...Object.keys(secretConfig)])).sort();
        return {
            category: stored?.category ?? template?.category ?? 'CUSTOM',
            provider: normalizedProvider,
            displayName: stored?.displayName ?? template?.displayName ?? normalizedProvider,
            enabled: stored?.enabled ?? true,
            configured: secretKeys.length
                ? secretKeys.some((key) => this.hasUsableValue(this.decryptOptional(secretConfig[key])))
                : Object.keys(publicConfig).length > 0,
            publicConfig,
            secrets: Object.fromEntries(secretKeys.map((key) => {
                const value = this.decryptOptional(secretConfig[key]);
                return [key, { configured: this.hasUsableValue(value), masked: value ? this.maskSecret(value) : null }];
            })),
            template: template
                ? {
                    publicKeys: template.publicKeys,
                    secretKeys: template.secretKeys,
                    description: template.description
                }
                : null,
            updatedById: stored?.updatedById ?? null,
            updatedAt: stored?.updatedAt ?? null
        };
    }
    cachedPublicValue(provider, key) {
        const value = this.cache.get((0, integration_config_types_1.normalizeIntegrationProvider)(provider))?.publicConfig[key];
        return value !== undefined && value !== null && value !== '' ? String(value) : undefined;
    }
    cachedSecretValue(provider, key) {
        const encrypted = this.cache.get((0, integration_config_types_1.normalizeIntegrationProvider)(provider))?.secretConfig[key];
        return encrypted ? this.decrypt(encrypted) : undefined;
    }
    normalizeCategory(value) {
        if (!value) {
            return undefined;
        }
        const normalized = value.trim().toUpperCase();
        if (['PAYMENT', 'SMS', 'EMAIL', 'CUSTOM'].includes(normalized)) {
            return normalized;
        }
        throw new common_1.BadRequestException('category must be PAYMENT, SMS, EMAIL, or CUSTOM.');
    }
    cleanPublicConfig(config) {
        if (!config) {
            return {};
        }
        return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== undefined && value !== null && value !== ''));
    }
    encryptSecretConfig(config) {
        if (!config) {
            return {};
        }
        return Object.fromEntries(Object.entries(config)
            .filter(([, value]) => value !== undefined && value !== null && value !== '')
            .map(([key, value]) => [key, this.encrypt(String(value))]));
    }
    encrypt(value) {
        const iv = (0, crypto_1.randomBytes)(12);
        const cipher = (0, crypto_1.createCipheriv)('aes-256-gcm', this.encryptionKey(), iv);
        const encrypted = Buffer.concat([cipher.update(value, 'utf8'), cipher.final()]);
        const tag = cipher.getAuthTag();
        return `v1:${iv.toString('base64')}:${tag.toString('base64')}:${encrypted.toString('base64')}`;
    }
    decrypt(value) {
        if (!value.startsWith('v1:')) {
            return value;
        }
        const [, ivRaw, tagRaw, encryptedRaw] = value.split(':');
        const decipher = (0, crypto_1.createDecipheriv)('aes-256-gcm', this.encryptionKey(), Buffer.from(ivRaw, 'base64'));
        decipher.setAuthTag(Buffer.from(tagRaw, 'base64'));
        return Buffer.concat([decipher.update(Buffer.from(encryptedRaw, 'base64')), decipher.final()]).toString('utf8');
    }
    decryptOptional(value) {
        if (!value) {
            return undefined;
        }
        try {
            return this.decrypt(value);
        }
        catch {
            return undefined;
        }
    }
    encryptionKey() {
        const secret = this.config.get('INTEGRATION_CONFIG_SECRET') ??
            this.config.get('JWT_REFRESH_SECRET') ??
            this.config.get('JWT_ACCESS_SECRET') ??
            'local-integration-config-secret';
        return (0, crypto_1.createHash)('sha256').update(secret).digest();
    }
    maskSecret(value) {
        if (value.length <= 4) {
            return '****';
        }
        return `****${value.slice(-4)}`;
    }
    jsonObject(value) {
        return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
    }
    stringMap(value) {
        const object = this.jsonObject(value);
        return Object.fromEntries(Object.entries(object).map(([key, item]) => [key, String(item)]));
    }
    toJson(value) {
        return JSON.parse(JSON.stringify(value));
    }
};
exports.IntegrationConfigService = IntegrationConfigService;
exports.IntegrationConfigService = IntegrationConfigService = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [prisma_service_1.PrismaService,
        config_1.ConfigService])
], IntegrationConfigService);
//# sourceMappingURL=integration-config.service.js.map