"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRedteamFilterDropdown = getRedteamFilterDropdown;
const drizzle_orm_1 = require("drizzle-orm");
const constants_1 = require("../../../constants");
const database_1 = require("../../../database");
const tables_1 = require("../../../database/tables");
const security_config_1 = require("./security-config");
function configDisplayName(name) {
    return typeof name === 'string' && name.trim() ? name.trim() : 'Unnamed config';
}
function configMatchesSearch(row, searchTrim) {
    if (!searchTrim) {
        return true;
    }
    const q = searchTrim.toLowerCase();
    const n = typeof row.name === 'string' ? row.name.toLowerCase() : '';
    const label = ((0, security_config_1.applicationNameFromSecurityConfig)(row.config) ?? '').toLowerCase();
    return n.includes(q) || label.includes(q);
}
function appNameMatchesSearch(name, searchTrim) {
    if (!searchTrim) {
        return false;
    }
    return (name ?? '').toLowerCase().includes(searchTrim.toLowerCase());
}
const MAX_APPLICATION_LIMIT = 100;
/**
 * Non-archived applications for the project (count capped by `applicationLimit` from the client),
 * each with all linked security configs (no cap on configs per app).
 * Optional `search` filters by application name and/or security config name / target label.
 * When `search` is empty, only applications with at least one linked config are returned.
 */
async function getRedteamFilterDropdown(params) {
    const { projectId, applicationLimit } = params;
    const searchTrim = typeof params.search === 'string' ? params.search.trim() : '';
    const pattern = searchTrim ? `%${searchTrim}%` : '';
    const appLimit = Math.min(Math.max(1, Math.floor(applicationLimit)), MAX_APPLICATION_LIMIT);
    const db = await (0, database_1.getDb)();
    const baseAppWhere = (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(tables_1.applicationsTable.projectId, projectId), (0, drizzle_orm_1.eq)(tables_1.applicationsTable.is_archived, false));
    const configBaseWhere = (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(tables_1.securityConfigTable.projectId, projectId), (0, drizzle_orm_1.eq)(tables_1.securityConfigTable.is_archived, false), (0, drizzle_orm_1.ne)(tables_1.securityConfigTable.status, constants_1.SECURITY_CONFIG_STATUS.DRAFT));
    const configWhere = searchTrim.length > 0
        ? (0, drizzle_orm_1.and)(configBaseWhere, (0, drizzle_orm_1.or)((0, drizzle_orm_1.ilike)(tables_1.securityConfigTable.name, pattern), (0, drizzle_orm_1.sql) `COALESCE(${tables_1.securityConfigTable.config}#>>'{target,label}', '') ILIKE ${pattern}`))
        : configBaseWhere;
    const configRows = await db
        .select({
        id: tables_1.securityConfigTable.id,
        name: tables_1.securityConfigTable.name,
        config: tables_1.securityConfigTable.config,
        projectId: tables_1.securityConfigTable.projectId,
    })
        .from(tables_1.securityConfigTable)
        .where(configWhere)
        .execute();
    const linkedMap = await (0, security_config_1.buildLinkedApplicationIdMap)(db, configRows);
    const enriched = configRows.map((row) => ({
        ...row,
        linkedApplicationId: (0, security_config_1.linkedApplicationIdFromMap)(linkedMap, row.projectId ?? null, row.config),
    }));
    let applicationRows;
    if (searchTrim.length === 0) {
        const appIdsWithConfigs = [
            ...new Set(enriched
                .map((c) => c.linkedApplicationId)
                .filter((id) => typeof id === 'string' && id.length > 0)),
        ];
        if (appIdsWithConfigs.length === 0) {
            applicationRows = [];
        }
        else {
            applicationRows = await db
                .select({
                id: tables_1.applicationsTable.id,
                name: tables_1.applicationsTable.name,
                updatedAt: tables_1.applicationsTable.updatedAt,
            })
                .from(tables_1.applicationsTable)
                .where((0, drizzle_orm_1.and)(baseAppWhere, (0, drizzle_orm_1.inArray)(tables_1.applicationsTable.id, appIdsWithConfigs)))
                .orderBy((0, drizzle_orm_1.desc)(tables_1.applicationsTable.updatedAt))
                .limit(appLimit)
                .execute();
        }
    }
    else {
        const nameMatchApps = await db
            .select({
            id: tables_1.applicationsTable.id,
            name: tables_1.applicationsTable.name,
            updatedAt: tables_1.applicationsTable.updatedAt,
        })
            .from(tables_1.applicationsTable)
            .where((0, drizzle_orm_1.and)(baseAppWhere, (0, drizzle_orm_1.ilike)(tables_1.applicationsTable.name, pattern)))
            .orderBy((0, drizzle_orm_1.desc)(tables_1.applicationsTable.updatedAt))
            .execute();
        const idsFromConfigs = new Set();
        for (const c of enriched) {
            if (c.linkedApplicationId) {
                idsFromConfigs.add(c.linkedApplicationId);
            }
        }
        const nameMatchIds = new Set(nameMatchApps.map((a) => a.id));
        const extraIds = [...idsFromConfigs].filter((id) => !nameMatchIds.has(id));
        let extraApps = [];
        if (extraIds.length > 0) {
            extraApps = await db
                .select({
                id: tables_1.applicationsTable.id,
                name: tables_1.applicationsTable.name,
                updatedAt: tables_1.applicationsTable.updatedAt,
            })
                .from(tables_1.applicationsTable)
                .where((0, drizzle_orm_1.and)(baseAppWhere, (0, drizzle_orm_1.inArray)(tables_1.applicationsTable.id, extraIds)))
                .execute();
        }
        const merged = new Map();
        for (const a of nameMatchApps) {
            merged.set(a.id, a);
        }
        for (const a of extraApps) {
            if (!merged.has(a.id)) {
                merged.set(a.id, a);
            }
        }
        const updatedAtMs = (u) => u instanceof Date ? u.getTime() : new Date(u).getTime();
        applicationRows = [...merged.values()].sort((a, b) => updatedAtMs(b.updatedAt) - updatedAtMs(a.updatedAt));
        if (applicationRows.length > appLimit) {
            applicationRows = applicationRows.slice(0, appLimit);
        }
    }
    const selectedAppIds = new Set(applicationRows.map((a) => a.id));
    const applications = applicationRows.map((app) => {
        const nameHit = appNameMatchesSearch(app.name, searchTrim);
        const configs = enriched
            .filter((c) => c.linkedApplicationId === app.id)
            .filter((c) => !searchTrim || configMatchesSearch(c, searchTrim) || nameHit)
            .map((c) => ({
            id: c.id,
            name: configDisplayName(c.name),
        }));
        return {
            id: app.id,
            name: app.name?.trim() ? String(app.name) : 'Unnamed application',
            configs,
        };
    });
    const applicationsWithConfigs = searchTrim.length === 0
        ? applications.filter((a) => a.configs.length > 0)
        : applications;
    const unassignedConfigs = enriched
        .filter((c) => !c.linkedApplicationId || !selectedAppIds.has(c.linkedApplicationId))
        .filter((c) => !searchTrim || configMatchesSearch(c, searchTrim))
        .map((c) => ({
        id: c.id,
        name: configDisplayName(c.name),
    }));
    return { applications: applicationsWithConfigs, unassignedConfigs };
}
//# sourceMappingURL=get-redteam-filter-dropdown.js.map