"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
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 __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
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); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IntegrationProfileService = void 0;
const api_pagination_1 = require("@hed-hog/api-pagination");
const api_prisma_1 = require("@hed-hog/api-prisma");
const common_1 = require("@nestjs/common");
const pako = __importStar(require("pako"));
const integration_profile_utils_1 = require("./integration-profile.utils");
let IntegrationProfileService = class IntegrationProfileService {
    constructor(prismaService, paginationService) {
        this.prismaService = prismaService;
        this.paginationService = paginationService;
    }
    async listTypes() {
        return this.prismaService.integration_type.findMany({
            orderBy: { id: 'asc' },
            select: {
                id: true,
                slug: true,
                icon: true,
                integration_type_locale: {
                    select: { name: true, locale: { select: { code: true } } },
                },
            },
        });
    }
    async listProviders(typeId, typeSlug) {
        const normalizedTypeSlug = String(typeSlug !== null && typeSlug !== void 0 ? typeSlug : '').trim();
        return this.prismaService.integration_provider.findMany({
            where: Object.assign(Object.assign({}, (typeId ? { type_id: typeId } : {})), (normalizedTypeSlug
                ? { integration_type: { slug: normalizedTypeSlug } }
                : {})),
            orderBy: { id: 'asc' },
            select: {
                id: true,
                slug: true,
                type_id: true,
                integration_provider_locale: {
                    select: { name: true, locale: { select: { code: true } } },
                },
            },
        });
    }
    async list(paginationParams, typeId, typeSlug, providerSlug) {
        const fields = ['slug', 'name'];
        const OR = this.prismaService.createInsensitiveSearch(fields, paginationParams);
        const normalizedTypeSlug = String(typeSlug !== null && typeSlug !== void 0 ? typeSlug : '').trim();
        const normalizedProviderSlug = String(providerSlug !== null && providerSlug !== void 0 ? providerSlug : '').trim();
        const result = await this.paginationService.paginate(this.prismaService.integration_profile, paginationParams, {
            where: Object.assign(Object.assign(Object.assign(Object.assign({}, (OR.length > 0 ? { OR } : {})), (typeId ? { type_id: typeId } : {})), (normalizedTypeSlug
                ? { integration_type: { slug: normalizedTypeSlug } }
                : {})), (normalizedProviderSlug
                ? { integration_provider: { slug: normalizedProviderSlug } }
                : {})),
            select: {
                id: true,
                slug: true,
                name: true,
                config: true,
                type_id: true,
                provider_id: true,
                is_active: true,
                created_at: true,
                updated_at: true,
                integration_type: {
                    select: {
                        slug: true,
                        icon: true,
                        integration_type_locale: {
                            select: { name: true, locale: { select: { code: true } } },
                        },
                    },
                },
                integration_provider: {
                    select: {
                        slug: true,
                        integration_provider_locale: {
                            select: { name: true, locale: { select: { code: true } } },
                        },
                    },
                },
            },
            orderBy: [{ updated_at: 'desc' }, { id: 'desc' }],
        });
        const items = Array.isArray(result.data) ? result.data : [];
        if (items.length > 0) {
            const ids = items.map((p) => p.id);
            const counts = await this.prismaService.$queryRaw `
        SELECT
          ip.id,
          (
            COALESCE((SELECT COUNT(*)::int FROM campaign       WHERE integration_profile_id = ip.id), 0) +
            COALESCE((SELECT COUNT(*)::int FROM webhook_action WHERE integration_profile_id = ip.id), 0)
          ) AS uses_count
        FROM integration_profile ip
        WHERE ip.id = ANY(${ids}::int[])
      `;
            const countMap = new Map(counts.map((r) => [r.id, Number(r.uses_count)]));
            result.data = items.map((p) => {
                var _a;
                return (Object.assign(Object.assign({}, p), { uses_count: (_a = countMap.get(p.id)) !== null && _a !== void 0 ? _a : 0 }));
            });
        }
        return result;
    }
    async get(id) {
        const profile = await this.prismaService.integration_profile.findUnique({
            where: { id },
            include: {
                integration_type: {
                    select: {
                        slug: true,
                        icon: true,
                        integration_type_locale: {
                            select: { name: true, locale: { select: { code: true } } },
                        },
                    },
                },
                integration_provider: {
                    select: {
                        slug: true,
                        integration_provider_locale: {
                            select: { name: true, locale: { select: { code: true } } },
                        },
                    },
                },
            },
        });
        if (!profile) {
            throw new common_1.NotFoundException(`Integration profile with id ${id} not found.`);
        }
        return profile;
    }
    async create(data) {
        var _a, _b;
        await this.ensureSlugIsAvailable(data.slug);
        await this.validateTypeAndProvider(data.type_id, data.provider_id);
        return this.prismaService.integration_profile.create({
            data: {
                slug: data.slug.trim(),
                name: data.name.trim(),
                type_id: data.type_id,
                provider_id: data.provider_id,
                config: (_a = data.config) !== null && _a !== void 0 ? _a : null,
                is_active: (_b = data.is_active) !== null && _b !== void 0 ? _b : true,
            },
        });
    }
    async update({ id, data }) {
        var _a, _b, _c, _d, _e, _f, _g, _h;
        const existing = await this.prismaService.integration_profile.findUnique({
            where: { id },
        });
        if (!existing) {
            throw new common_1.NotFoundException(`Integration profile with id ${id} not found.`);
        }
        if (data.slug && data.slug !== existing.slug) {
            await this.ensureSlugIsAvailable(data.slug);
        }
        const type_id = (_a = data.type_id) !== null && _a !== void 0 ? _a : existing.type_id;
        const provider_id = (_b = data.provider_id) !== null && _b !== void 0 ? _b : existing.provider_id;
        await this.validateTypeAndProvider(type_id, provider_id);
        return this.prismaService.integration_profile.update({
            where: { id },
            data: {
                slug: (_d = (_c = data.slug) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : existing.slug,
                name: (_f = (_e = data.name) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : existing.name,
                type_id,
                provider_id,
                config: data.config !== undefined
                    ? ((_g = data.config) !== null && _g !== void 0 ? _g : null)
                    : existing.config,
                is_active: (_h = data.is_active) !== null && _h !== void 0 ? _h : existing.is_active,
            },
        });
    }
    async delete({ ids }) {
        if (!(ids === null || ids === void 0 ? void 0 : ids.length)) {
            throw new common_1.BadRequestException('You must select at least one item to delete.');
        }
        return this.prismaService.integration_profile.deleteMany({
            where: { id: { in: ids } },
        });
    }
    async test(data) {
        var _a, _b;
        await this.validateTypeAndProvider(data.type_id, data.provider_id);
        const provider = await this.prismaService.integration_provider.findUnique({
            where: { id: data.provider_id },
            select: {
                slug: true,
                integration_type: {
                    select: {
                        slug: true,
                    },
                },
            },
        });
        if (!provider) {
            throw new common_1.BadRequestException(`Provider with id ${data.provider_id} not found.`);
        }
        if (provider.integration_type.slug !== 'email') {
            throw new common_1.BadRequestException('Integration profile test is only available for email type.');
        }
        (0, integration_profile_utils_1.buildMailConfigFromIntegration)(provider.slug, data.config);
        const destination = String((_b = (_a = data.config) === null || _a === void 0 ? void 0 : _a.from_email) !== null && _b !== void 0 ? _b : '').trim();
        if (!destination) {
            throw new common_1.BadRequestException('Sender email is required.');
        }
        return {
            success: true,
            destination,
        };
    }
    async exportProfiles({ ids, includeSecrets = false, }) {
        const rows = await this.prismaService.integration_profile.findMany({
            where: (ids === null || ids === void 0 ? void 0 : ids.length) ? { id: { in: ids } } : undefined,
            select: {
                slug: true,
                name: true,
                config: true,
                is_active: true,
                integration_type: {
                    select: { slug: true },
                },
                integration_provider: {
                    select: { slug: true },
                },
            },
            orderBy: [{ slug: 'asc' }],
        });
        const payload = rows.map((row) => ({
            slug: row.slug,
            name: row.name,
            type_slug: row.integration_type.slug,
            provider_slug: row.integration_provider.slug,
            config: this.sanitizeConfigForExport(row.integration_provider.slug, row.config, includeSecrets),
            is_active: row.is_active,
        }));
        const envelope = {
            type: 'integration-profile',
            version: 1,
            generated_at: new Date().toISOString(),
            include_secrets: includeSecrets,
            data: payload,
        };
        const compressed = pako.gzip(JSON.stringify(envelope));
        return Buffer.from(compressed);
    }
    async validateImportFile(file) {
        const envelope = this.parseImportEnvelope(file);
        const typeMap = new Map((await this.prismaService.integration_type.findMany({
            select: { id: true, slug: true },
        })).map((row) => [row.slug.toLowerCase(), row]));
        const providerMap = new Map((await this.prismaService.integration_provider.findMany({
            select: { id: true, slug: true, type_id: true },
        })).map((row) => [row.slug.toLowerCase(), row]));
        const firstSlugIndex = new Map();
        const validItems = [];
        const invalidItems = [];
        envelope.data.forEach((item, index) => {
            var _a, _b, _c, _d, _e, _f;
            const errors = [];
            const slug = String((_a = item === null || item === void 0 ? void 0 : item.slug) !== null && _a !== void 0 ? _a : '').trim();
            const name = String((_b = item === null || item === void 0 ? void 0 : item.name) !== null && _b !== void 0 ? _b : '').trim();
            const typeSlug = String((_c = item === null || item === void 0 ? void 0 : item.type_slug) !== null && _c !== void 0 ? _c : '')
                .trim()
                .toLowerCase();
            const providerSlug = String((_d = item === null || item === void 0 ? void 0 : item.provider_slug) !== null && _d !== void 0 ? _d : '')
                .trim()
                .toLowerCase();
            if (!slug)
                errors.push('Missing slug.');
            if (!name)
                errors.push(`Missing name for slug "${slug || index}".`);
            if (!typeSlug)
                errors.push(`Missing type_slug for slug "${slug || index}".`);
            if (!providerSlug) {
                errors.push(`Missing provider_slug for slug "${slug || index}".`);
            }
            if (slug) {
                if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
                    errors.push(`Invalid slug format "${slug}".`);
                }
                if (firstSlugIndex.has(slug)) {
                    const originalIndex = firstSlugIndex.get(slug);
                    errors.push(`Duplicate slug "${slug}" in file (already used at index ${originalIndex}).`);
                }
                else {
                    firstSlugIndex.set(slug, index);
                }
            }
            const type = typeMap.get(typeSlug);
            if (!type && typeSlug) {
                errors.push(`Unknown type_slug "${typeSlug}".`);
            }
            const provider = providerMap.get(providerSlug);
            if (!provider && providerSlug) {
                errors.push(`Unknown provider_slug "${providerSlug}".`);
            }
            if (type && provider && provider.type_id !== type.id) {
                errors.push(`Provider "${providerSlug}" does not belong to type "${typeSlug}".`);
            }
            if ((item === null || item === void 0 ? void 0 : item.config) !== null &&
                (item === null || item === void 0 ? void 0 : item.config) !== undefined &&
                (typeof item.config !== 'object' || Array.isArray(item.config))) {
                errors.push(`Invalid config for slug "${slug || index}".`);
            }
            if (errors.length > 0) {
                invalidItems.push({ index, slug: slug || undefined, errors });
                return;
            }
            validItems.push({
                index,
                slug,
                name,
                type_slug: typeSlug,
                provider_slug: providerSlug,
                config: (_e = item.config) !== null && _e !== void 0 ? _e : null,
                is_active: (_f = item.is_active) !== null && _f !== void 0 ? _f : true,
            });
        });
        const existingSlugs = new Set((await this.prismaService.integration_profile.findMany({
            where: {
                slug: {
                    in: validItems.map((item) => item.slug),
                },
            },
            select: { slug: true },
        })).map((row) => row.slug));
        const profiles = validItems.map((item) => (Object.assign(Object.assign({}, item), { has_conflict: existingSlugs.has(item.slug) })));
        const conflictSlugs = profiles
            .filter((item) => item.has_conflict)
            .map((item) => item.slug);
        return {
            format: {
                type: envelope.type,
                version: envelope.version,
                generated_at: envelope.generated_at,
                include_secrets: envelope.include_secrets,
            },
            total_profiles: envelope.data.length,
            valid_profiles: profiles.length,
            invalid_profiles: invalidItems.length,
            conflict_profiles: conflictSlugs.length,
            importable_profiles: profiles.length - conflictSlugs.length,
            conflict_slugs: conflictSlugs,
            profiles,
            invalid_items: invalidItems,
        };
    }
    async confirmImport({ profiles, conflictResolutions, }) {
        var _a, _b, _c;
        if (!(profiles === null || profiles === void 0 ? void 0 : profiles.length)) {
            throw new common_1.BadRequestException('No profiles provided for import.');
        }
        const resolutionMap = new Map((conflictResolutions !== null && conflictResolutions !== void 0 ? conflictResolutions : []).map((resolution) => [
            resolution.slug,
            resolution.action,
        ]));
        const typeMap = new Map((await this.prismaService.integration_type.findMany({
            select: { id: true, slug: true },
        })).map((row) => [row.slug.toLowerCase(), row]));
        const providerMap = new Map((await this.prismaService.integration_provider.findMany({
            select: { id: true, slug: true, type_id: true },
        })).map((row) => [row.slug.toLowerCase(), row]));
        const duplicatedSlugs = new Set();
        const seenSlugs = new Set();
        profiles.forEach((item) => {
            const slug = item.slug.trim();
            if (seenSlugs.has(slug)) {
                duplicatedSlugs.add(slug);
            }
            seenSlugs.add(slug);
        });
        if (duplicatedSlugs.size > 0) {
            throw new common_1.BadRequestException(`File has duplicated slugs: ${Array.from(duplicatedSlugs).join(', ')}`);
        }
        const existingBySlug = new Map((await this.prismaService.integration_profile.findMany({
            where: { slug: { in: profiles.map((profile) => profile.slug.trim()) } },
            select: { id: true, slug: true },
        })).map((row) => [row.slug, row]));
        let created = 0;
        let replaced = 0;
        let ignored = 0;
        let renamed = 0;
        for (const item of profiles) {
            const slug = item.slug.trim();
            const name = item.name.trim();
            const typeSlug = item.type_slug.trim().toLowerCase();
            const providerSlug = item.provider_slug.trim().toLowerCase();
            if (!slug || !name || !typeSlug || !providerSlug) {
                throw new common_1.BadRequestException(`Invalid profile payload for slug "${slug || '<empty>'}".`);
            }
            const type = typeMap.get(typeSlug);
            if (!type) {
                throw new common_1.BadRequestException(`Unknown type_slug "${typeSlug}".`);
            }
            const provider = providerMap.get(providerSlug);
            if (!provider) {
                throw new common_1.BadRequestException(`Unknown provider_slug "${providerSlug}".`);
            }
            if (provider.type_id !== type.id) {
                throw new common_1.BadRequestException(`Provider "${providerSlug}" does not belong to type "${typeSlug}".`);
            }
            const existing = existingBySlug.get(slug);
            const data = {
                slug,
                name,
                type_id: type.id,
                provider_id: provider.id,
                config: (_a = item.config) !== null && _a !== void 0 ? _a : null,
                is_active: (_b = item.is_active) !== null && _b !== void 0 ? _b : true,
            };
            if (!existing) {
                await this.prismaService.integration_profile.create({ data });
                created += 1;
                continue;
            }
            const action = (_c = resolutionMap.get(slug)) !== null && _c !== void 0 ? _c : 'ignore';
            if (action === 'ignore') {
                ignored += 1;
                continue;
            }
            if (action === 'replace') {
                await this.prismaService.integration_profile.update({
                    where: { id: existing.id },
                    data: {
                        name: data.name,
                        type_id: data.type_id,
                        provider_id: data.provider_id,
                        config: data.config,
                        is_active: data.is_active,
                    },
                });
                replaced += 1;
                continue;
            }
            const newSlug = await this.generateUniqueSlug(slug);
            await this.prismaService.integration_profile.create({
                data: Object.assign(Object.assign({}, data), { slug: newSlug }),
            });
            renamed += 1;
        }
        return {
            success: true,
            total_received: profiles.length,
            created,
            replaced,
            renamed,
            ignored,
            imported: created + replaced + renamed,
        };
    }
    async ensureSlugIsAvailable(slug) {
        const existing = await this.prismaService.integration_profile.findUnique({
            where: { slug },
            select: { id: true },
        });
        if (existing) {
            throw new common_1.BadRequestException(`Integration profile with slug "${slug}" already exists.`);
        }
    }
    async validateTypeAndProvider(type_id, provider_id) {
        const provider = await this.prismaService.integration_provider.findUnique({
            where: { id: provider_id },
            select: { id: true, type_id: true },
        });
        if (!provider) {
            throw new common_1.BadRequestException(`Provider with id ${provider_id} not found.`);
        }
        if (provider.type_id !== type_id) {
            throw new common_1.BadRequestException(`Provider ${provider_id} does not belong to type ${type_id}.`);
        }
    }
    parseImportEnvelope(file) {
        var _a, _b;
        let parsed;
        try {
            const decompressed = pako.ungzip(file.buffer, { to: 'string' });
            parsed = JSON.parse(decompressed);
        }
        catch (_c) {
            throw new common_1.BadRequestException('File is not a valid .hedhog archive or is corrupted.');
        }
        if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
            throw new common_1.BadRequestException('Invalid .hedhog payload format.');
        }
        const payload = parsed;
        if (payload.type !== 'integration-profile') {
            throw new common_1.BadRequestException(`Invalid .hedhog type "${String((_a = payload.type) !== null && _a !== void 0 ? _a : '')}".`);
        }
        if (payload.version !== 1) {
            throw new common_1.BadRequestException(`Unsupported .hedhog version "${String((_b = payload.version) !== null && _b !== void 0 ? _b : '')}".`);
        }
        if (!Array.isArray(payload.data)) {
            throw new common_1.BadRequestException('Invalid .hedhog data: expected an array.');
        }
        return {
            type: 'integration-profile',
            version: 1,
            generated_at: typeof payload.generated_at === 'string'
                ? payload.generated_at
                : new Date().toISOString(),
            include_secrets: payload.include_secrets === true,
            data: payload.data,
        };
    }
    sanitizeConfigForExport(providerSlug, config, includeSecrets) {
        if (!config || includeSecrets)
            return config;
        const hiddenKeys = this.getSensitiveConfigKeys(providerSlug);
        if (hiddenKeys.length === 0)
            return config;
        const sanitized = Object.assign({}, config);
        hiddenKeys.forEach((key) => {
            delete sanitized[key];
        });
        return sanitized;
    }
    getSensitiveConfigKeys(providerSlug) {
        var _a;
        const map = {
            smtp: ['password'],
            gmail: ['client_secret', 'refresh_token'],
            ses: ['secret_access_key'],
            'evolution-api': ['token'],
            'whatsapp-official': ['access_token'],
            s3: ['secret_access_key'],
            gcs: ['key_file_json'],
            'azure-blob': ['key'],
            's3-compatible': ['secret_access_key'],
            openai: ['api_key'],
            gemini: ['api_key'],
            claude: ['api_key'],
            'google-oauth': ['client_secret'],
            'github-oauth': ['client_secret'],
            'microsoft-oauth': ['client_secret'],
        };
        return (_a = map[providerSlug.toLowerCase()]) !== null && _a !== void 0 ? _a : [];
    }
    async generateUniqueSlug(baseSlug) {
        let candidate = `${baseSlug}-copy`;
        let suffix = 1;
        // Gera slug único determinístico para imports com conflito e opção de renomear.
        while (await this.prismaService.integration_profile.findUnique({
            where: { slug: candidate },
            select: { id: true },
        })) {
            candidate = `${baseSlug}-copy-${suffix}`;
            suffix += 1;
        }
        return candidate;
    }
};
exports.IntegrationProfileService = IntegrationProfileService;
exports.IntegrationProfileService = IntegrationProfileService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_prisma_1.PrismaService))),
    __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_pagination_1.PaginationService))),
    __metadata("design:paramtypes", [api_prisma_1.PrismaService,
        api_pagination_1.PaginationService])
], IntegrationProfileService);
//# sourceMappingURL=integration-profile.service.js.map