/**
 * LangChain Integration for ShieldCortex
 *
 * Provides two exports:
 * - ShieldCortexMemory: A BaseMemory-compatible class that scans content through
 *   the defence pipeline before storing memories.
 * - ShieldCortexGuard: A standalone scanner that can be used as middleware
 *   in any LangChain chain without storing memories.
 *
 * Usage:
 *   import { ShieldCortexMemory, ShieldCortexGuard } from 'shieldcortex/integrations/langchain';
 *
 *   // As a memory backend
 *   const memory = new ShieldCortexMemory({ mode: 'balanced' });
 *   const vars = await memory.loadMemoryVariables({});
 *   await memory.saveContext({ input: 'hello' }, { output: 'hi' });
 *
 *   // As a standalone guard
 *   const guard = new ShieldCortexGuard();
 *   const result = guard.scan('some suspicious content');
 *   if (!result.allowed) { ... }
 */
import { runDefencePipeline } from '../defence/pipeline.js';
import { DEFAULT_DEFENCE_CONFIG } from '../defence/types.js';
import { addMemory, searchMemories, getRecentMemories, } from '../memory/store.js';
// ── ShieldCortexMemory ──
/**
 * LangChain-compatible memory class that runs the defence pipeline
 * on all content before storage. Blocked content is quarantined.
 *
 * Implements the same interface as LangChain's BaseMemory:
 * - memoryVariables: string[]
 * - loadMemoryVariables(values): Promise<Record<string, string>>
 * - saveContext(inputValues, outputValues): Promise<void>
 * - clear(): Promise<void>
 */
export class ShieldCortexMemory {
    config;
    source;
    maxResults;
    _memoryKey;
    project;
    allowClear;
    /** Keys this memory populates (LangChain interface) */
    get memoryVariables() {
        return [this._memoryKey];
    }
    constructor(options = {}) {
        this.config = {
            ...DEFAULT_DEFENCE_CONFIG,
            mode: options.mode ?? 'balanced',
        };
        this.source = {
            type: options.sourceType ?? 'agent',
            identifier: options.sourceIdentifier ?? 'langchain-agent',
        };
        this.maxResults = options.maxResults ?? 10;
        this._memoryKey = options.memoryKey ?? 'history';
        this.project = options.project;
        this.allowClear = options.allowClear ?? false;
    }
    /**
     * Load relevant memories. Searches by recent context or returns recent memories.
     */
    async loadMemoryVariables(values) {
        const query = values.input ?? values.query ?? values.question;
        let text;
        if (query && typeof query === 'string') {
            const results = await searchMemories({
                query,
                limit: this.maxResults,
                project: this.project,
            });
            text = results
                .map((r) => `[${r.memory.title}] ${r.memory.content}`)
                .join('\n');
        }
        else {
            const recent = getRecentMemories(this.maxResults);
            text = recent
                .map((m) => `[${m.title}] ${m.content}`)
                .join('\n');
        }
        return { [this._memoryKey]: text };
    }
    /**
     * Save conversation context. Runs the defence pipeline before storing.
     * Blocked content is automatically quarantined.
     */
    async saveContext(inputValues, outputValues) {
        const input = String(inputValues.input ?? inputValues.query ?? '');
        const output = String(outputValues.output ?? outputValues.response ?? '');
        const content = `User: ${input}\nAssistant: ${output}`;
        const title = input.slice(0, 100);
        const memoryInput = {
            title,
            content,
            type: 'episodic',
            category: 'context',
            project: this.project,
        };
        // addMemory runs defence pipeline internally when source is provided
        try {
            addMemory(memoryInput, undefined, this.source);
        }
        catch (err) {
            // MemoryBlockedError means it was quarantined — that's expected
            if (err instanceof Error && err.name === 'MemoryBlockedError') {
                return;
            }
            throw err;
        }
    }
    /**
     * Clear memories. Disabled by default for safety.
     * Pass { allowClear: true } to constructor to enable.
     * For targeted deletion, use the MCP forget tool or REST API instead.
     */
    async clear() {
        if (!this.allowClear) {
            throw new Error('ShieldCortexMemory.clear() is disabled by default for safety. ' +
                'Use the MCP "forget" tool or REST API for targeted deletion, ' +
                'or pass { allowClear: true } to the constructor to enable this method.');
        }
        // If explicitly enabled, this is a no-op for now since we don't have
        // a way to clear only memories created by this instance.
        // Callers who need this should use the forget tool directly.
    }
}
/**
 * Standalone content scanner. Does NOT store memories.
 * Use this as middleware to scan content before passing it to any memory backend.
 */
export class ShieldCortexGuard {
    config;
    source;
    constructor(options = {}) {
        this.config = {
            ...DEFAULT_DEFENCE_CONFIG,
            mode: options.mode ?? 'balanced',
        };
        this.source = {
            type: options.sourceType ?? 'agent',
            identifier: options.sourceIdentifier ?? 'langchain-guard',
        };
    }
    /**
     * Scan content through the full defence pipeline.
     * Returns the pipeline result — check result.allowed.
     */
    scan(content, title, source) {
        return runDefencePipeline(content, title ?? 'Untitled', source ?? this.source, this.config);
    }
    /**
     * Convenience: returns true if content is safe.
     */
    isAllowed(content, title) {
        return this.scan(content, title).allowed;
    }
}
