"use strict";
/**
 * JetBrainsAiIdeAdapter -- JetBrains AI Assistant adapter.
 *
 * Covers the JetBrains AI Assistant across IntelliJ IDEA, PyCharm,
 * WebStorm, GoLand, Rider, DataGrip, and the full JetBrains IDE family.
 *
 * Hook surfaces:
 *   completionRequested(file, cursor, context) -- pre-completion gate.
 *   completionReceived(completion, context) -- post-completion ingress.
 *   chatRequested(prompt, context) -- pre-chat gate.
 *   chatReceived(response) -- post-chat ingress classification.
 *   suggestionAccepted(suggestion, targetFile) -- audit event on acceptance.
 *   suggestionRejected(suggestion, reason) -- optional audit event.
 *
 * JetBrains-specific surfaces:
 *   refactorRequested(targetSymbol, intent) -- JetBrains AI has native
 *     refactor-with-AI; pre-refactor gate catches PHI or secrets in the
 *     symbol's context. (PT-ADAPT-JBAI-1)
 *   commitMessageRequested(diff) -- AI-generated commit message; classify
 *     diff for secret leaks BEFORE the message generation. (PT-ADAPT-JBAI-2)
 *   testGenerationRequested(function, context) -- AI-generated tests;
 *     ensure no production-data fixtures leak into generated tests.
 *
 * JetBrains-specific considerations:
 *   - JetBrains AI uses the JetBrains AI Cloud by default but supports
 *     local Ollama + OpenAI + Anthropic as alternative backends.
 *     Adapter reads the backend choice per-request and routes through
 *     provider-compliance.
 *   - DataGrip workflows often touch production databases directly. When
 *     DataGrip is the host IDE, adapter applies stricter data-class rules
 *     for result-set classification. (PT-ADAPT-JBAI-3)
 *
 * Spec: P3-SUB-IDE-03
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.JetBrainsAiIdeAdapter = exports.JETBRAINS_BAA_ELIGIBLE_BACKENDS = void 0;
const base_1 = require("./base");
const index_1 = require("./index");
const shared_1 = require("./shared");
/**
 * Backends that carry a JetBrains data-processing agreement meeting
 * HIPAA BAA requirements.
 *
 * JetBrains Cloud: JetBrains Inc. must have an active BAA in place.
 * OpenAI/Anthropic/Azure: customer must hold their own BAA with the provider.
 * Ollama: self-hosted -- no third-party BAA needed; customer controls data.
 *
 * For the purposes of this adapter, only the backends where JetBrains itself
 * would be the processor are explicitly gated. Self-hosted (Ollama) is
 * treated as BAA-compliant by default because the customer controls residency.
 */
exports.JETBRAINS_BAA_ELIGIBLE_BACKENDS = [
    'ollama', // self-hosted, customer controls data
    'openai', // customer holds OpenAI BAA
    'anthropic', // customer holds Anthropic BAA
    'azure-openai', // customer holds Microsoft BAA (common in healthcare M365 tenants)
];
// ---------------------------------------------------------------------------
// DataGrip PHI column name patterns
//
// When DataGrip is the host IDE, we apply a stricter data-class gate on
// result-set column names before allowing AI analysis of query results.
// ---------------------------------------------------------------------------
const DATAGRIP_PHI_COLUMN_PATTERNS = [
    /\bpatient[_]?(id|name|ssn|dob|mrn|record)\b/i,
    /\bsocial[_]?security\b/i,
    /\bssn\b/,
    /\bdate[_]?of[_]?birth\b/i,
    /\bdob\b/,
    /\bdiagnos[ie]/i,
    /\btreatment\b/i,
    /\bmedical[_]?record\b/i,
    /\bhipaa\b/i,
    /\bphi\b/,
    /\bcard[_]?number\b/i, // PCI
    /\bpan\b/, // Primary Account Number (PCI)
    /\bcvv\b/i,
];
// ---------------------------------------------------------------------------
// Secret patterns (shared with GitHub Copilot context, inline here for
// adapter portability)
// ---------------------------------------------------------------------------
const JB_SECRET_PATTERNS = [
    { name: 'AWS access key', re: /AKIA[0-9A-Z]{16}/ },
    { name: 'GitHub PAT', re: /ghp_[0-9A-Za-z]{36}|github_pat_[0-9A-Za-z_]{82}/ },
    { name: 'Anthropic API key', re: /sk-ant-[0-9A-Za-z\-_]{20,}/ },
    { name: 'OpenAI API key', re: /sk-[0-9A-Za-z]{20,}/ },
    { name: 'Private key header', re: /-----BEGIN (RSA|EC|OPENSSH) PRIVATE KEY-----/ },
    { name: 'Generic secret env var', re: /(SECRET|PASSWORD|PASSWD|API_KEY|PRIVATE_KEY)\s*=\s*["']?\S{8,}["']?/i },
    { name: 'Bearer token', re: /Authorization:\s*Bearer\s+[0-9A-Za-z\-._~+/]{20,}/i },
    { name: 'Database URL with creds', re: /(postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/ },
];
// ---------------------------------------------------------------------------
// PHI proximity indicators
// ---------------------------------------------------------------------------
const JB_PHI_INDICATORS = [
    /\bpatient[_\s]?(id|name|dob|ssn|mrn|record)\b/i,
    /\bdiagnos(is|es)\b/i,
    /\bprescription\b/i,
    /\bmedical[_\s]?record\b/i,
    /\bhealth[_\s]?plan\b/i,
    /\bsocial[_\s]?security\b/i,
    /\bssn\b/,
    /\bdate[_\s]?of[_\s]?birth\b/i,
    /\btreatment[_\s]?plan\b/i,
    /\bhipaa\b/i,
    /\bphi\b/,
];
// ---------------------------------------------------------------------------
// JetBrainsAiIdeAdapter
// ---------------------------------------------------------------------------
class JetBrainsAiIdeAdapter extends base_1.BaseIdeAdapter {
    adapterId = 'jetbrains-ai';
    ideId = 'jetbrains-ai';
    jbConfig = null;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        super.initialize(config);
        this.jbConfig = config;
        const backend = this.jbConfig.aiBackend ?? 'jetbrains-cloud';
        const variant = this.jbConfig.ideVariant ?? 'unknown';
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:session-open', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            detail: `JetBrainsAiIdeAdapter initialized. ` +
                `Backend: ${backend}. ` +
                `IDE variant: ${variant}. ` +
                `DataGrip strict mode: ${variant === 'datagrip'}.`,
            data: { backend, ideVariant: variant, dataGripMode: variant === 'datagrip' },
        }));
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    //
    // JetBrains identity is provided by the IDE authentication layer (JetBrains
    // Account, GitHub, or SSO). The shim provides this as the userId.
    // ---------------------------------------------------------------------------
    async resolveUserIdentity(session) {
        this.ensureInitialized();
        const fromSession = session.identity?.userId?.trim();
        if (fromSession) {
            const identity = {
                userId: fromSession,
                email: session.identity?.email,
                orgId: session.identity?.orgId,
                githubLogin: session.identity?.githubLogin,
                attributionMethod: 'cursor-composer', // closest available attribution type for IDE-level identity
            };
            this.emitIdentityResolved(identity.userId, session.clientSessionId);
            return identity;
        }
        const allowAnon = this.config?.allowAnonymousIdeSessions === true;
        if (allowAnon) {
            const anon = { userId: 'anonymous', attributionMethod: 'cursor-composer' };
            this.emitIdentityResolved(anon.userId, session.clientSessionId);
            return anon;
        }
        this.emitIdentityUnresolved(session.clientSessionId, 'JetBrainsAiIdeAdapter: no user identity in session context.');
        throw new index_1.IdeAttributionError({
            message: 'JetBrainsAiIdeAdapter: cannot resolve user identity. Provide a userId in ' +
                'session.identity.userId (the JetBrains Account login or SSO ID), or set ' +
                'allowAnonymousIdeSessions: true in adapter config.',
            ideId: this.ideId,
            adapterId: this.adapterId,
        });
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation
    //
    // Normalised tool invocations map the JetBrains AI hook surfaces:
    //   Standard six (shared with GitHub Copilot contract):
    //   'jb:completion-requested'       -> completionRequested gate
    //   'jb:completion-received'        -> completionReceived ingress
    //   'jb:chat-requested'             -> chatRequested gate
    //   'jb:chat-received'              -> chatReceived ingress
    //   'jb:suggestion-accepted'        -> audit event
    //   'jb:suggestion-rejected'        -> audit event
    //
    //   JetBrains-specific:
    //   'jb:refactor-requested'         -> refactorRequested gate (PT-ADAPT-JBAI-1)
    //   'jb:commit-message-requested'   -> commitMessageRequested gate (PT-ADAPT-JBAI-2)
    //   'jb:test-generation-requested'  -> testGenerationRequested gate
    //   'jb:datagrip-result-requested'  -> DataGrip result-set analysis gate (PT-ADAPT-JBAI-3)
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(ctx) {
        this.ensureInitialized();
        switch (ctx.tool) {
            case 'jb:completion-requested':
                return this._gateCompletionRequest(ctx);
            case 'jb:completion-received':
                return this._scanCompletionResponse(ctx);
            case 'jb:chat-requested':
                return this._gateChatRequest(ctx);
            case 'jb:chat-received':
                return this._scanChatResponse(ctx);
            case 'jb:suggestion-accepted':
                return this._auditSuggestionAccepted(ctx);
            case 'jb:suggestion-rejected':
                return this._auditSuggestionRejected(ctx);
            case 'jb:refactor-requested':
                return this._gateRefactorRequest(ctx);
            case 'jb:commit-message-requested':
                return this._gateCommitMessageRequest(ctx);
            case 'jb:test-generation-requested':
                return this._gateTestGenerationRequest(ctx);
            case 'jb:datagrip-result-requested':
                return this._gateDataGripResultRequest(ctx);
            default:
                this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    sessionId: ctx.sessionHandle.sessionId,
                    initiatingUserId: ctx.initiatingUser.userId,
                    tool: ctx.tool,
                    detail: `JetBrainsAiIdeAdapter: unknown surface '${ctx.tool}' -- allowed with audit.`,
                }));
                return this.routeToolInvocation(ctx, {
                    decision: 'ALLOW',
                    reason: `Unknown JetBrains AI surface '${ctx.tool}' permitted with audit.`,
                });
        }
    }
    // ---------------------------------------------------------------------------
    // Hook: completionRequested
    // ---------------------------------------------------------------------------
    async _gateCompletionRequest(ctx) {
        const surroundingContext = String(ctx.input['surroundingContext'] ?? ctx.input['context'] ?? '');
        const filePath = String(ctx.input['filePath'] ?? ctx.input['file_path'] ?? '');
        const secretMatch = this._scanForSecrets(surroundingContext);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Completion blocked: secret pattern '${secretMatch}' in surrounding context (${filePath}).`,
                metadata: { filePath, secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(surroundingContext);
            if (phiMatch) {
                const tier = this.jbConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Completion routed for HIPAA review: PHI indicator '${phiMatch}' in context. Tier: ${tier}.`,
                    metadata: { filePath, phiIndicator: phiMatch, tier },
                });
            }
        }
        const backendDecision = this._checkBackendCompliance(ctx);
        if (backendDecision)
            return backendDecision;
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Completion request permitted by JetBrainsAiIdeAdapter.`,
            metadata: { filePath },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: completionReceived
    // ---------------------------------------------------------------------------
    async _scanCompletionResponse(ctx) {
        const completionText = String(ctx.input['completionText'] ?? ctx.input['completion'] ?? '');
        const secretMatch = this._scanForSecrets(completionText);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Completion response blocked: secret pattern '${secretMatch}' in returned code.`,
                metadata: { secretPattern: secretMatch },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Completion response passed JetBrains AI secret-scan ingress.',
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: chatRequested
    // ---------------------------------------------------------------------------
    async _gateChatRequest(ctx) {
        const promptText = String(ctx.input['promptText'] ?? ctx.input['prompt'] ?? '');
        const attachedFilePaths = ctx.input['attachedFilePaths'] ?? [];
        const secretMatch = this._scanForSecrets(promptText);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Chat request blocked: secret pattern '${secretMatch}' in prompt.`,
                metadata: { secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(promptText);
            if (phiMatch) {
                const tier = this.jbConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Chat request routed for HIPAA review: PHI indicator '${phiMatch}'. Tier: ${tier}.`,
                    metadata: { phiIndicator: phiMatch, tier },
                });
            }
        }
        const backendDecision = this._checkBackendCompliance(ctx);
        if (backendDecision)
            return backendDecision;
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Chat request permitted by JetBrainsAiIdeAdapter.',
            metadata: { attachedFiles: attachedFilePaths.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: chatReceived
    // ---------------------------------------------------------------------------
    async _scanChatResponse(ctx) {
        const responseText = String(ctx.input['responseText'] ?? ctx.input['response'] ?? '');
        const secretMatch = this._scanForSecrets(responseText);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Chat response blocked: secret pattern '${secretMatch}' in response.`,
                metadata: { secretPattern: secretMatch },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Chat response passed JetBrains AI ingress scan.',
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: suggestionAccepted
    // ---------------------------------------------------------------------------
    async _auditSuggestionAccepted(ctx) {
        const targetFilePath = String(ctx.input['targetFilePath'] ?? '');
        const completionId = String(ctx.input['completionId'] ?? '');
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            sessionId: ctx.sessionHandle.sessionId,
            initiatingUserId: ctx.initiatingUser.userId,
            tool: ctx.tool,
            detail: `Developer accepted a JetBrains AI suggestion. Target: ${targetFilePath}. CompletionId: ${completionId || 'n/a'}.`,
            data: { targetFilePath, completionId },
        }));
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Suggestion acceptance audited for '${targetFilePath}'.`,
            metadata: { targetFilePath, completionId },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: suggestionRejected
    // ---------------------------------------------------------------------------
    async _auditSuggestionRejected(ctx) {
        const reason = String(ctx.input['reason'] ?? 'not-specified');
        const completionId = String(ctx.input['completionId'] ?? '');
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            sessionId: ctx.sessionHandle.sessionId,
            initiatingUserId: ctx.initiatingUser.userId,
            tool: ctx.tool,
            detail: `Developer rejected a JetBrains AI suggestion. Reason: ${reason}. CompletionId: ${completionId || 'n/a'}.`,
            data: { reason, completionId },
        }));
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Suggestion rejection audited. Reason: ${reason}.`,
        });
    }
    // ---------------------------------------------------------------------------
    // JetBrains-specific Hook: refactorRequested (PT-ADAPT-JBAI-1)
    //
    // JetBrains AI has a native "Refactor with AI" feature. This gate checks
    // the target symbol context for PHI or secrets BEFORE the refactor AI
    // prompt is sent to the backend.
    // ---------------------------------------------------------------------------
    async refactorRequested(ctx) {
        this.ensureInitialized();
        const combinedContext = `${ctx.targetSymbol} ${ctx.intent} ${ctx.surroundingContext ?? ''}`;
        // 1. Secret scan
        const secretMatch = this._scanForSecrets(combinedContext);
        if (secretMatch) {
            const decision = {
                decision: 'DENY',
                reason: `Refactor request blocked: secret pattern '${secretMatch}' found in target symbol ` +
                    `context (${ctx.filePath ?? 'unknown file'}). Remove the secret before requesting AI refactoring.`,
                metadata: { targetSymbol: ctx.targetSymbol, secretPattern: secretMatch },
            };
            this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                tool: 'jb:refactor-requested',
                detail: decision.reason,
                data: { targetSymbol: ctx.targetSymbol, filePath: ctx.filePath },
            }));
            return decision;
        }
        // 2. PHI proximity check under HIPAA binding (PT-ADAPT-JBAI-1)
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(combinedContext);
            if (phiMatch) {
                const tier = this.jbConfig?.refactorReviewerTier ?? 'T2_LEAD';
                const decision = {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Refactor request blocked under HIPAA binding: ` +
                        `PHI indicator '${phiMatch}' found in target symbol '${ctx.targetSymbol}' context. ` +
                        `Reviewer tier: ${tier}. ` +
                        `Ensure the refactoring plan is reviewed before the AI prompt is sent.`,
                    metadata: { targetSymbol: ctx.targetSymbol, phiIndicator: phiMatch, tier },
                };
                this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-require-approval', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    tool: 'jb:refactor-requested',
                    detail: decision.reason,
                    data: { targetSymbol: ctx.targetSymbol, filePath: ctx.filePath, phiIndicator: phiMatch },
                }));
                return decision;
            }
        }
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            tool: 'jb:refactor-requested',
            detail: `Refactor request permitted for symbol '${ctx.targetSymbol}' (${ctx.filePath ?? 'unknown'}).`,
            data: { targetSymbol: ctx.targetSymbol, intent: ctx.intent },
        }));
        return {
            decision: 'ALLOW',
            reason: `Refactor request permitted by JetBrainsAiIdeAdapter for '${ctx.targetSymbol}'.`,
        };
    }
    // ---------------------------------------------------------------------------
    // JetBrains-specific Hook: commitMessageRequested (PT-ADAPT-JBAI-2)
    //
    // JetBrains AI can generate commit messages from diffs. This gate classifies
    // the diff for secret leaks BEFORE the message generation prompt is sent.
    // ---------------------------------------------------------------------------
    async commitMessageRequested(ctx) {
        this.ensureInitialized();
        // Scan the diff for secrets
        const secretMatch = this._scanDiffForSecrets(ctx.diff);
        if (secretMatch) {
            const decision = {
                decision: 'DENY',
                reason: `Commit message generation blocked: secret pattern '${secretMatch.pattern}' ` +
                    `detected in diff at line ${secretMatch.line}. ` +
                    `Remove the secret from the staged changes before generating a commit message. ` +
                    `Branch: ${ctx.branchName ?? 'unknown'}.`,
                metadata: {
                    branchName: ctx.branchName,
                    secretPattern: secretMatch.pattern,
                    diffLine: secretMatch.line,
                },
            };
            this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                tool: 'jb:commit-message-requested',
                detail: decision.reason,
                data: { branchName: ctx.branchName, secretPattern: secretMatch.pattern },
            }));
            return decision;
        }
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            tool: 'jb:commit-message-requested',
            detail: `Commit message generation permitted. Branch: ${ctx.branchName ?? 'unknown'}. Diff passed secret scan.`,
            data: { branchName: ctx.branchName },
        }));
        return {
            decision: 'ALLOW',
            reason: `Commit message generation permitted. Diff passed secret scan.`,
            metadata: { branchName: ctx.branchName },
        };
    }
    // ---------------------------------------------------------------------------
    // JetBrains-specific Hook: testGenerationRequested
    //
    // AI-generated tests must not incorporate production-data fixtures that
    // contain PHI or secrets. This gate scans the function and its context.
    // ---------------------------------------------------------------------------
    async testGenerationRequested(ctx) {
        this.ensureInitialized();
        const combined = `${ctx.targetFunction} ${ctx.surroundingContext ?? ''}`;
        const secretMatch = this._scanForSecrets(combined);
        if (secretMatch) {
            return {
                decision: 'DENY',
                reason: `Test generation blocked: secret pattern '${secretMatch}' in function context. ` +
                    `Production credentials must not appear in generated test fixtures.`,
                metadata: { targetFunction: ctx.targetFunction, secretPattern: secretMatch },
            };
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(combined);
            if (phiMatch) {
                const tier = this.jbConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Test generation routed for HIPAA review: PHI indicator '${phiMatch}' in function context. ` +
                        `Ensure generated tests use synthetic data, not production fixtures. Tier: ${tier}.`,
                    metadata: { targetFunction: ctx.targetFunction, phiIndicator: phiMatch, tier },
                };
            }
        }
        return {
            decision: 'ALLOW',
            reason: `Test generation permitted for '${ctx.targetFunction}'. Context passed checks.`,
        };
    }
    // ---------------------------------------------------------------------------
    // JetBrains-specific Hook: DataGrip result-set gate (PT-ADAPT-JBAI-3)
    //
    // When DataGrip is the host IDE, AI analysis of query result sets must be
    // gated. Column names that indicate PHI or PAN must be flagged and the
    // result-set must not be sent to the AI backend without tagging.
    // ---------------------------------------------------------------------------
    async dataGripResultSetRequested(ctx) {
        this.ensureInitialized();
        const ideVariant = this.jbConfig?.ideVariant ?? 'unknown';
        // DataGrip strict mode: column-level PHI check
        // Also applied if caller explicitly passes column data regardless of IDE variant
        const phiColumns = [];
        for (const col of ctx.columns) {
            for (const pattern of DATAGRIP_PHI_COLUMN_PATTERNS) {
                if (pattern.test(col)) {
                    phiColumns.push(col);
                    break;
                }
            }
        }
        if (phiColumns.length > 0) {
            const isHipaa = this._isHipaaBinding();
            const tier = this.jbConfig?.hipaaReviewerTier ?? 'T2_LEAD';
            if (isHipaa) {
                const decision = {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `DataGrip result-set analysis blocked under HIPAA binding: ` +
                        `columns [${phiColumns.join(', ')}] indicate PHI data. ` +
                        `Reviewer tier: ${tier}. ` +
                        `Tag the data class before allowing AI analysis. IDE: ${ideVariant}.`,
                    metadata: { phiColumns, ideVariant, tier },
                };
                this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-require-approval', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    tool: 'jb:datagrip-result-requested',
                    detail: decision.reason,
                    data: { phiColumns, query: ctx.query, ideVariant },
                }));
                return decision;
            }
            // Non-HIPAA pack: warn but allow, emit WARN audit event
            this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                tool: 'jb:datagrip-result-requested',
                detail: `DataGrip result-set contains PHI-indicator columns [${phiColumns.join(', ')}] ` +
                    `but no HIPAA pack binding is active. Allowed with WARN. IDE: ${ideVariant}.`,
                data: { phiColumns, ideVariant },
            }));
            return {
                decision: 'ALLOW',
                reason: `Result-set analysis permitted (no HIPAA binding). ` +
                    `PHI-indicator columns present: [${phiColumns.join(', ')}]. ` +
                    `Consider adding a HIPAA pack binding to enforce classification.`,
                metadata: { phiColumns, ideVariant },
            };
        }
        return {
            decision: 'ALLOW',
            reason: `DataGrip result-set analysis permitted. No PHI-indicator columns found. IDE: ${ideVariant}.`,
            metadata: { ideVariant },
        };
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation dispatch for JetBrains-specific surfaces
    // (called when surface events come through the normalised tool invocation path)
    // ---------------------------------------------------------------------------
    async _gateRefactorRequest(ctx) {
        return this.refactorRequested({
            targetSymbol: String(ctx.input['targetSymbol'] ?? ctx.input['symbol'] ?? ''),
            intent: String(ctx.input['intent'] ?? ''),
            surroundingContext: ctx.input['surroundingContext'],
            filePath: ctx.input['filePath'],
            sessionId: ctx.sessionHandle.sessionId,
        });
    }
    async _gateCommitMessageRequest(ctx) {
        return this.commitMessageRequested({
            diff: String(ctx.input['diff'] ?? ''),
            branchName: ctx.input['branchName'],
            sessionId: ctx.sessionHandle.sessionId,
        });
    }
    async _gateTestGenerationRequest(ctx) {
        return this.testGenerationRequested({
            targetFunction: String(ctx.input['targetFunction'] ?? ctx.input['function'] ?? ''),
            surroundingContext: ctx.input['surroundingContext'],
            filePath: ctx.input['filePath'],
            sessionId: ctx.sessionHandle.sessionId,
        });
    }
    async _gateDataGripResultRequest(ctx) {
        return this.dataGripResultSetRequested({
            columns: ctx.input['columns'] ?? [],
            query: ctx.input['query'],
            sessionId: ctx.sessionHandle.sessionId,
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getIdeCapabilities() {
        return {
            runtimeHooks: true,
            prBoundary: false,
            userIdentity: true,
            reviewerRouting: true,
            integrationMode: 'jetbrains-ai-normalised-event-shim',
        };
    }
    // ---------------------------------------------------------------------------
    // Error translation
    // ---------------------------------------------------------------------------
    translateError(raw) {
        if (raw instanceof index_1.IdeAttributionError)
            return raw;
        if (raw instanceof index_1.IdeUnsupportedError)
            return raw;
        return super.translateError(raw);
    }
    // ---------------------------------------------------------------------------
    // Helpers
    // ---------------------------------------------------------------------------
    _scanForSecrets(text) {
        if (!text)
            return null;
        for (const { name, re } of JB_SECRET_PATTERNS) {
            if (re.test(text))
                return name;
        }
        return null;
    }
    _scanForPhiIndicators(text) {
        if (!text)
            return null;
        for (const re of JB_PHI_INDICATORS) {
            if (re.test(text))
                return re.source;
        }
        return null;
    }
    _isHipaaBinding() {
        const packId = this.config?.packId ?? '';
        return packId === 'hipaa' || packId.startsWith('hipaa');
    }
    /**
     * Scan a unified diff for secret patterns. Returns the first finding
     * or null. Only scans added lines (lines starting with '+').
     */
    _scanDiffForSecrets(diff) {
        const lines = diff.split('\n');
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (!line || !line.startsWith('+') || line.startsWith('+++'))
                continue;
            const content = line.slice(1);
            for (const { name, re } of JB_SECRET_PATTERNS) {
                if (re.test(content)) {
                    return { line: i + 1, pattern: name };
                }
            }
        }
        return null;
    }
    /**
     * Check backend-level compliance for the active pack binding.
     * Returns a DENY decision if the backend is not BAA-eligible under HIPAA,
     * or null if no issue.
     */
    _checkBackendCompliance(ctx) {
        if (!this._isHipaaBinding())
            return null;
        const backend = this.jbConfig?.aiBackend ?? 'jetbrains-cloud';
        if (!exports.JETBRAINS_BAA_ELIGIBLE_BACKENDS.includes(backend)) {
            return {
                decision: 'DENY',
                reason: `JetBrainsAiIdeAdapter: backend '${backend}' is not on the BAA-eligible list ` +
                    `for HIPAA pack binding '${this.config?.packId}'. ` +
                    `Switch to ollama (self-hosted), openai, anthropic, or azure-openai, ` +
                    `and ensure a BAA is in place with the provider before using AI assistance ` +
                    `on HIPAA-regulated code.`,
                metadata: {
                    aiBackend: backend,
                    hipaaPackId: this.config?.packId,
                    eligibleBackends: exports.JETBRAINS_BAA_ELIGIBLE_BACKENDS,
                },
            };
        }
        return null;
    }
    /** Expose the resolved backend for diagnostics / tests. */
    getAiBackend() {
        return this.jbConfig?.aiBackend ?? 'jetbrains-cloud';
    }
    /** Expose the IDE variant for diagnostics / tests. */
    getIdeVariant() {
        return this.jbConfig?.ideVariant ?? 'unknown';
    }
}
exports.JetBrainsAiIdeAdapter = JetBrainsAiIdeAdapter;
//# sourceMappingURL=jetbrains-ai.js.map