/**
 * Auto-Pack Generator (W2.3 of F-NEW-PRODUCT-AGENT-VISIBILITY-CLUSTER-2026-05-12).
 *
 * Closes the loop between W1.7 (OWASP Agentic scanner) and W1.5 (capability-
 * class company packs). Customer scans an agent manifest -> auto-pack
 * generator proposes a company pack -> customer accepts / edits / rejects
 * each rule inline -> resulting pack is publishable.
 *
 * Customer flow:
 *   const manifest = ... ;                                // from W2.1/W2.2 discovery
 *   const { pack, scanReport, proposedRules } =
 *     autoGenerateCompanyPack(manifest, { packIdPrefix: 'agent-mailer' });
 *   // Customer reviews scanReport.findings + proposedRules in the dashboard,
 *   // selects which rules to accept, optionally edits actions:
 *   const acceptedRules = customerSelection;
 *   const final = autoGenerateCompanyPack(manifest, {
 *     packIdPrefix: 'agent-mailer',
 *     acceptedRules,
 *   }).pack;
 *   // final is a CompliancePackWithClassifiers ready to publish via
 *   // PackVersionStore.publishVersion().
 *
 * HITL Phase 2b (2026-05-21): perRuleActions baking.
 *   Pass options.perRuleActions (keyed {packId: {findingId: {action, notify_hitl?}}})
 *   and options.scanFindings (the raw OwaspFinding[] from the scan) to bake
 *   customer-selected per-rule actions AND notify_hitl flags into the generated
 *   pack's categoryActions and categoryHitl maps. The categoryHitl map is then
 *   read at runtime by evaluateAgentBoundPacks() to trigger HITL dispatch.
 *
 * @connexum/ai-governance  F-NEW-PRODUCT-AUTO-PACK-GENERATOR
 */
import { scanAgent } from './owasp-agentic-scanner.js';
import { buildCompanyPack, } from './company-pack-builder.js';
const ORG_FROM_MANIFEST = (m) => {
    // The manifest doesn't currently carry orgId; in the discovery flow
    // (W2.1/W2.2) the caller knows the org. For the slice we default to
    // 'org-unknown' and require callers to override at activation time.
    return m.orgId ?? 'org-unknown';
};
export function autoGenerateCompanyPack(manifest, options = {}) {
    const scanReport = scanAgent(manifest);
    const proposedRules = scanReport.suggestedRules;
    const acceptedRules = options.acceptedRules
        ? [...options.acceptedRules]
        : options.acceptAllSuggestedRules
            ? [...proposedRules]
            : [];
    const reviewRequiredRisks = Array.from(new Set(scanReport.findings
        .filter((f) => !f.suggestedRule)
        .map((f) => f.risk)));
    const packIdPrefix = options.packIdPrefix ?? 'auto-pack';
    const packId = `${packIdPrefix}-${manifest.agentId}`;
    // ---------------------------------------------------------------------------
    // HITL Phase 2b: bake perRuleActions overrides into acceptedRules and build
    // a categoryHitl map to attach to the generated pack's dataPolicy.
    //
    // perRuleActions is keyed { packId: { findingId: { action, notify_hitl? } } }.
    // scanFindings provides the findingId → capability bridge via each finding's
    // suggestedRule.capability field. When both are supplied, we:
    //   1. For each findingId in the override, locate the matching OwaspFinding.
    //   2. Read the suggested capability from finding.suggestedRule.capability.
    //   3. Override the action in acceptedRules (or add the rule if absent).
    //   4. Record notify_hitl in categoryHitl[capability].
    // ---------------------------------------------------------------------------
    const categoryHitl = {};
    if (options.perRuleActions && options.scanFindings && options.scanFindings.length > 0) {
        // Build a quick lookup: findingId → OwaspFinding.
        // Findings do not carry an explicit id field in the current schema, so we
        // use the finding's risk + title as a stable composite key. The caller
        // (onboarding-routes.ts) stores per_rule_actions keyed by the findingId
        // string which is generated at wizard Step 4 time. We support the case
        // where the findingId matches the finding's suggestedRule capability
        // (the dashboard uses capability as the stable key for Step 4 selections).
        const findingByCapability = new Map();
        for (const finding of options.scanFindings) {
            if (finding.suggestedRule?.capability) {
                findingByCapability.set(finding.suggestedRule.capability, finding);
            }
        }
        // Iterate the per-pack overrides for this pack.
        const packOverrides = options.perRuleActions[packId] ?? {};
        for (const [findingId, override] of Object.entries(packOverrides)) {
            // findingId is the capability string when sourced from the dashboard
            // Step 4 picker (the UI uses capability as the stable identifier).
            const finding = findingByCapability.get(findingId);
            if (!finding || !finding.suggestedRule)
                continue;
            const capability = finding.suggestedRule.capability;
            // Override the action in acceptedRules (mutate the list in place).
            const existingIdx = acceptedRules.findIndex((r) => r.capability === capability);
            const validActions = ['ALLOW', 'LOG', 'WARN', 'REQUIRE_APPROVAL', 'BLOCK'];
            const overrideAction = override.action;
            if (validActions.includes(overrideAction)) {
                if (existingIdx >= 0) {
                    acceptedRules[existingIdx] = { ...acceptedRules[existingIdx], action: overrideAction };
                }
                else {
                    acceptedRules.push({ capability, action: overrideAction });
                }
            }
            // Bake the notify_hitl flag.
            if (override.notify_hitl === true) {
                categoryHitl[capability] = true;
            }
        }
    }
    const pack = buildCompanyPack({
        id: packId,
        name: `Auto-generated pack for ${manifest.name ?? manifest.agentId}`,
        orgId: ORG_FROM_MANIFEST(manifest),
        version: options.version ?? '0.1.0',
        rules: acceptedRules,
        isolation: options.isolation,
        description: `Auto-generated from OWASP Agentic scan on ${new Date().toISOString().slice(0, 10)}. ` +
            `${scanReport.findings.length} findings (CRIT=${scanReport.countsBySeverity.CRIT}, ` +
            `HIGH=${scanReport.countsBySeverity.HIGH}, MED=${scanReport.countsBySeverity.MED}, ` +
            `LOW=${scanReport.countsBySeverity.LOW}). Build-structure score: ` +
            `${scanReport.buildStructureScore}. ${acceptedRules.length}/${proposedRules.length} ` +
            `rules accepted by customer.`,
    });
    // Attach categoryHitl to the pack's dataPolicy (non-empty only).
    // This makes it readable by evaluateAgentBoundPacks() at runtime without
    // any schema migration — it's carried alongside categoryActions in the
    // serialised pack blob stored by PackVersionStore.
    if (Object.keys(categoryHitl).length > 0) {
        pack.dataPolicy.categoryHitl = categoryHitl;
    }
    return {
        pack,
        scanReport,
        proposedRules,
        acceptedRules,
        reviewRequiredRisks,
        categoryHitl,
    };
}
//# sourceMappingURL=auto-pack-generator.js.map