"use strict";
/**
 * IngestClassifier -- P4-RET-01
 *
 * Wires DataClassifier at the audit-event ingress point.
 *
 * classifyAndTag() is the single entry point for all audit events entering
 * the retention engine. It:
 *   1. Resolves the DataClassTag for the event using DataClassifier.classify().
 *   2. Attaches dataClass[] to the event metadata.
 *   3. Enforces the PHI-without-HIPAA block: if the resolved dataClass is
 *      PhiRecord and 'hipaa' is not in activePackIds, the call throws
 *      PhiWithoutHipaaError (fail-closed -- no PHI enters the system without
 *      an active HIPAA binding).
 *   4. Returns the enriched event with dataClass stamped.
 *
 * PT-RET-5 (Unicode lookalike evasion):
 *   The classifier matches write-site strings against the rule table using
 *   exact-string and RegExp matching only. Write-site strings are
 *   normalised to NFC Unicode before matching so Unicode lookalike attacks
 *   (e.g. using Cyrillic 'а' U+0430 to impersonate ASCII 'a' U+0061) are
 *   detected by the rule-miss guard and throw UnclassifiedWriteError.
 *
 * @connexum/ai-governance P4-RET-01
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnboundDataError = exports.UnclassifiedWriteError = exports.PhiWithoutHipaaError = void 0;
exports.getClassifier = getClassifier;
exports.__resetClassifierForTest = __resetClassifierForTest;
exports.classifyAndTag = classifyAndTag;
const classifier_1 = require("./classifier");
Object.defineProperty(exports, "UnboundDataError", { enumerable: true, get: function () { return classifier_1.UnboundDataError; } });
Object.defineProperty(exports, "UnclassifiedWriteError", { enumerable: true, get: function () { return classifier_1.UnclassifiedWriteError; } });
const data_class_1 = require("./data-class");
// ---------------------------------------------------------------------------
// PHI-without-HIPAA error
// ---------------------------------------------------------------------------
/**
 * Thrown when a PHI record arrives without an active HIPAA binding.
 *
 * Fail-closed: PHI must not enter the system without HIPAA governance.
 * The calling layer must activate the hipaa compliance pack for the tenant
 * before storing any protected health information.
 *
 * PT-RET-5 / P4-RET-01 AC 6.
 */
class PhiWithoutHipaaError extends classifier_1.RetentionError {
    tenantId;
    constructor(tenantId) {
        super({
            message: `PhiWithoutHipaaError: PHI record detected for tenant "${tenantId}" ` +
                'but the hipaa compliance pack is not active. ' +
                'Activate the hipaa pack for this tenant before storing protected health information. ' +
                '(P4-RET-01 AC 6, PT-RET-5)',
            code: 'PHI_WITHOUT_HIPAA',
        });
        this.name = 'PhiWithoutHipaaError';
        this.tenantId = tenantId;
    }
}
exports.PhiWithoutHipaaError = PhiWithoutHipaaError;
// ---------------------------------------------------------------------------
// Singleton classifier instance
// ---------------------------------------------------------------------------
let _classifier = null;
/**
 * Return (or create) the shared DataClassifier instance.
 * Custom rules can be injected via `options.rules` in tests.
 */
function getClassifier(options) {
    if (options?.rules) {
        // Non-singleton path for tests: always create a fresh instance.
        return new classifier_1.DataClassifier({ rules: options.rules });
    }
    if (!_classifier) {
        _classifier = new classifier_1.DataClassifier();
    }
    return _classifier;
}
/**
 * Reset the shared DataClassifier instance. For tests only.
 * @internal
 */
function __resetClassifierForTest() {
    _classifier = null;
}
// ---------------------------------------------------------------------------
// Main API
// ---------------------------------------------------------------------------
/**
 * Classify an incoming audit event and stamp a DataClassTag onto it.
 *
 * Wraps DataClassifier.classify() with:
 *   - NFC Unicode normalisation of writeSite (PT-RET-5 lookalike guard)
 *   - PHI-without-HIPAA enforcement (PhiWithoutHipaaError)
 *   - dataClass[] attachment to the result
 *
 * Throws:
 *   - UnclassifiedWriteError: write site has no rule (fail-closed)
 *   - UnboundDataError: resolved packBinding is empty
 *   - PhiWithoutHipaaError: PHI record without active HIPAA pack
 *
 * @param event  The audit event to enrich (not mutated).
 * @param ctx    Classification context.
 * @param opts   Optional: inject custom classifier rules for tests.
 */
function classifyAndTag(event, ctx, opts) {
    // PT-RET-5: normalise writeSite to NFC Unicode to defeat lookalike substitutions.
    // A Cyrillic 'а' (U+0430) in the write-site string will not match any
    // ASCII rule and will hit the UnclassifiedWriteError guard.
    const normalisedWriteSite = ctx.writeSite.normalize('NFC');
    const classifier = getClassifier(opts);
    // May throw UnclassifiedWriteError or UnboundDataError.
    const tag = classifier.classify(normalisedWriteSite, {
        tenantId: ctx.tenantId,
        contractId: ctx.contractId,
        activePackIds: ctx.activePackIds,
        sovereigntyHint: ctx.sovereigntyHint,
    });
    // PHI-without-HIPAA block (P4-RET-01 AC 6, PT-RET-5).
    if (tag.dataClass === data_class_1.DataClass.PhiRecord && !ctx.activePackIds.includes('hipaa')) {
        throw new PhiWithoutHipaaError(ctx.tenantId);
    }
    return {
        event,
        tag,
        dataClass: [tag.dataClass],
    };
}
//# sourceMappingURL=ingest-classifier.js.map