/**
 * Provenance Management MCP Tools
 *
 * Extracted from src/index.ts Task 22.
 * Tools: ocr_provenance_get, ocr_provenance_verify, ocr_provenance_export,
 *         ocr_provenance_query, ocr_provenance_timeline, ocr_provenance_processor_stats
 *
 * CRITICAL: NEVER use console.log() - stdout is reserved for JSON-RPC protocol.
 * Use console.error() for all logging.
 *
 * @module tools/provenance
 */
import { z } from 'zod';
import { requireDatabase } from '../server/state.js';
import { successResult } from '../server/types.js';
import { validateInput, ProvenanceGetInput, ProvenanceVerifyInput, ProvenanceExportInput, } from '../utils/validation.js';
import { provenanceNotFoundError, validationError, documentNotFoundError, } from '../server/errors.js';
import { formatResponse, handleError } from './shared.js';
import { getImage } from '../services/storage/database/image-operations.js';
import { getOCRResult } from '../services/storage/database/ocr-operations.js';
import { ProvenanceVerifier } from '../services/provenance/verifier.js';
import { ProvenanceTracker } from '../services/provenance/tracker.js';
// ═══════════════════════════════════════════════════════════════════════════════
// HELPER FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════════
/**
 * Escape a field for CSV output.
 * If the field contains a comma, double-quote, or newline, wrap it in double quotes
 * and escape internal double-quotes by doubling them.
 */
function csvEscape(field) {
    if (field === null || field === undefined)
        return '';
    const str = String(field);
    if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
        return `"${str.replace(/"/g, '""')}"`;
    }
    return str;
}
/**
 * Find provenance ID from an item of any type.
 * Returns the provenance ID and detected item type, or null if not found.
 */
function findProvenanceId(db, itemId) {
    const doc = db.getDocument(itemId);
    if (doc)
        return { provenanceId: doc.provenance_id, itemType: 'document' };
    const chunk = db.getChunk(itemId);
    if (chunk)
        return { provenanceId: chunk.provenance_id, itemType: 'chunk' };
    const embedding = db.getEmbedding(itemId);
    if (embedding)
        return { provenanceId: embedding.provenance_id, itemType: 'embedding' };
    const dbConn = db.getConnection();
    const image = getImage(dbConn, itemId);
    if (image && image.provenance_id) {
        return { provenanceId: image.provenance_id, itemType: 'image' };
    }
    const ocrResult = getOCRResult(dbConn, itemId);
    if (ocrResult && ocrResult.provenance_id) {
        return { provenanceId: ocrResult.provenance_id, itemType: 'ocr_result' };
    }
    const comparison = dbConn
        .prepare('SELECT provenance_id FROM comparisons WHERE id = ?')
        .get(itemId);
    if (comparison) {
        return { provenanceId: comparison.provenance_id, itemType: 'comparison' };
    }
    const cluster = dbConn.prepare('SELECT provenance_id FROM clusters WHERE id = ?').get(itemId);
    if (cluster) {
        return { provenanceId: cluster.provenance_id, itemType: 'clustering' };
    }
    const formFill = dbConn
        .prepare('SELECT provenance_id FROM form_fills WHERE id = ?')
        .get(itemId);
    if (formFill) {
        return { provenanceId: formFill.provenance_id, itemType: 'form_fill' };
    }
    const extraction = dbConn
        .prepare('SELECT provenance_id FROM extractions WHERE id = ?')
        .get(itemId);
    if (extraction) {
        return { provenanceId: extraction.provenance_id, itemType: 'extraction' };
    }
    const prov = db.getProvenance(itemId);
    if (prov)
        return { provenanceId: prov.id, itemType: 'provenance' };
    return null;
}
// ═══════════════════════════════════════════════════════════════════════════════
// PROVENANCE TOOL HANDLERS
// ═══════════════════════════════════════════════════════════════════════════════
export async function handleProvenanceGet(params) {
    try {
        const input = validateInput(ProvenanceGetInput, params);
        const { db } = requireDatabase();
        const found = findProvenanceId(db, input.item_id);
        const provenanceId = found?.provenanceId ?? null;
        const itemType = found?.itemType ?? input.item_type ?? 'auto';
        if (!provenanceId) {
            throw provenanceNotFoundError(input.item_id);
        }
        const chain = db.getProvenanceChain(provenanceId);
        if (chain.length === 0) {
            throw provenanceNotFoundError(input.item_id);
        }
        // Get the root document ID from the chain (last element is the root after upward walk)
        const rootDocId = chain[chain.length - 1].root_document_id;
        // Also fetch ALL descendants from root to build full tree
        const allRecords = rootDocId ? db.getProvenanceByRootDocument(rootDocId) : [];
        const toProvenanceSummary = (p) => ({
            id: p.id,
            type: p.type,
            chain_depth: p.chain_depth,
            processor: p.processor,
            processor_version: p.processor_version,
            content_hash: p.content_hash,
            created_at: p.created_at,
            parent_id: p.parent_id,
        });
        const enrichedChain = chain.map(toProvenanceSummary);
        // Build descendants tree (excluding items already in the upward chain)
        const chainIds = new Set(chain.map((c) => c.id));
        const allDescendants = allRecords.filter((r) => !chainIds.has(r.id));
        const result = {
            item_id: input.item_id,
            item_type: itemType,
            chain: enrichedChain,
            root_document_id: chain[0].root_document_id,
        };
        const descLimit = input.descendants_limit ?? 50;
        if (input.include_descendants) {
            // Return actual descendant records, sliced to limit
            const sliced = allDescendants.slice(0, descLimit).map(toProvenanceSummary);
            result.descendants = sliced;
            result.descendants_returned = sliced.length;
            result.descendants_total = allDescendants.length;
            result.has_more_descendants = allDescendants.length > descLimit;
        }
        else {
            // Default: summary counts by type
            const byType = {};
            for (const d of allDescendants) {
                byType[d.type] = (byType[d.type] || 0) + 1;
            }
            result.descendants_summary = {
                total: allDescendants.length,
                by_type: byType,
            };
        }
        result.total_records = enrichedChain.length + allDescendants.length;
        result.next_steps = [
            {
                tool: 'ocr_provenance_get',
                description: 'Get descendant records: include_descendants=true',
            },
            { tool: 'ocr_provenance_verify', description: 'Verify content integrity of this provenance chain' },
            { tool: 'ocr_document_get', description: 'Read the source document for full context' },
            { tool: 'ocr_chunk_get', description: 'Read a specific chunk by ID' },
        ];
        return formatResponse(successResult(result));
    }
    catch (error) {
        return handleError(error);
    }
}
/**
 * Handle ocr_provenance_verify - Verify the integrity of an item through its provenance chain
 *
 * Uses real ProvenanceVerifier to re-hash content and compare against stored hashes.
 * Constitution CP-003: SHA-256 hashes at every processing step enable tamper detection.
 */
export async function handleProvenanceVerify(params) {
    try {
        const input = validateInput(ProvenanceVerifyInput, params);
        const { db } = requireDatabase();
        const found = findProvenanceId(db, input.item_id);
        if (!found) {
            throw provenanceNotFoundError(input.item_id);
        }
        const provenanceId = found.provenanceId;
        // Use real ProvenanceVerifier for content integrity verification
        const tracker = new ProvenanceTracker(db);
        const verifier = new ProvenanceVerifier(db, tracker);
        // Verify the full chain (re-hashes content at each step)
        const chainResult = await verifier.verifyChain(provenanceId);
        // Build per-step details for the response
        const chain = db.getProvenanceChain(provenanceId);
        const steps = [];
        const errors = [];
        let chainIntegrity = chainResult.chain_intact;
        for (let i = 0; i < chain.length; i++) {
            const prov = chain[i];
            const step = {
                provenance_id: prov.id,
                type: prov.type,
                chain_depth: prov.chain_depth,
                content_verified: true,
                chain_verified: true,
                expected_hash: prov.content_hash,
            };
            // Check if this item failed content verification
            if (input.verify_content) {
                const failedItem = chainResult.failed_items.find((f) => f.id === prov.id);
                if (failedItem) {
                    step.content_verified = false;
                    step.computed_hash = failedItem.computed_hash;
                    errors.push(`Content hash mismatch at ${prov.id}: expected ${failedItem.expected_hash}, got ${failedItem.computed_hash}`);
                }
            }
            // Verify chain structure (depth and parent links)
            // H-5: Only verify depths when chain is intact; incomplete chains produce wrong expected depths
            if (input.verify_chain) {
                if (chainResult.chain_intact) {
                    const expectedDepth = chain.length - 1 - i;
                    if (prov.chain_depth !== expectedDepth) {
                        step.chain_verified = false;
                        chainIntegrity = false;
                        errors.push(`Chain depth mismatch at ${prov.id}: expected ${expectedDepth}, got ${prov.chain_depth}`);
                    }
                    if (i > 0 && chain[i - 1].parent_id !== prov.id) {
                        step.chain_verified = false;
                        chainIntegrity = false;
                        errors.push(`Parent link broken at ${chain[i - 1].id}`);
                    }
                }
                else {
                    step.chain_verified = false;
                }
            }
            steps.push(step);
        }
        // H-5: When chain is not intact, add a note instead of false-positive depth errors
        if (input.verify_chain && !chainResult.chain_intact) {
            errors.push('Depth verification skipped: chain is incomplete');
        }
        // Verify descendants (not in upward chain) for full bidirectional verification
        const rootDocId = chain[chain.length - 1].root_document_id;
        const allRecords = rootDocId ? db.getProvenanceByRootDocument(rootDocId) : [];
        const chainIds = new Set(chain.map((c) => c.id));
        const descendantRecords = allRecords.filter((r) => !chainIds.has(r.id));
        let descendantsVerified = 0;
        let descendantsFailed = 0;
        if (input.verify_content) {
            for (const record of descendantRecords) {
                try {
                    const descResult = await verifier.verifyContentHash(record.id);
                    const step = {
                        provenance_id: record.id,
                        type: record.type,
                        chain_depth: record.chain_depth,
                        content_verified: descResult.valid,
                        expected_hash: record.content_hash,
                    };
                    if (!descResult.valid) {
                        step.computed_hash = descResult.computed_hash;
                        descendantsFailed++;
                        errors.push(`Content hash mismatch at descendant ${record.id} (${record.type}): ` +
                            `expected ${descResult.expected_hash}, got ${descResult.computed_hash}`);
                    }
                    else {
                        descendantsVerified++;
                    }
                    steps.push(step);
                }
                catch (verifyError) {
                    descendantsFailed++;
                    const errMsg = verifyError instanceof Error ? verifyError.message : String(verifyError);
                    errors.push(`Failed to verify descendant ${record.id} (${record.type}): ${errMsg}`);
                    steps.push({
                        provenance_id: record.id,
                        type: record.type,
                        chain_depth: record.chain_depth,
                        content_verified: false,
                        error: errMsg,
                    });
                }
            }
        }
        // M-9: When verify_content is false, skip content hash result entirely
        const contentIntegrity = input.verify_content
            ? chainResult.hashes_failed === 0 && descendantsFailed === 0
            : true;
        const result = {
            item_id: input.item_id,
            verified: contentIntegrity && chainIntegrity,
            content_integrity: contentIntegrity,
            chain_integrity: chainIntegrity,
            steps,
            errors: errors.length > 0 ? errors : undefined,
        };
        // M-9: Only include hash counts when content verification was requested
        if (input.verify_content) {
            result.hashes_verified = chainResult.hashes_verified + descendantsVerified;
            result.hashes_failed = chainResult.hashes_failed + descendantsFailed;
            result.descendants_verified = descendantsVerified;
            result.descendants_failed = descendantsFailed;
            result.total_items_checked =
                chainResult.hashes_verified + chainResult.hashes_failed + descendantsVerified + descendantsFailed;
        }
        result.next_steps = [
            { tool: 'ocr_provenance_get', description: 'View the full provenance chain' },
            { tool: 'ocr_document_get', description: 'Get details for the root document' },
            { tool: 'ocr_reprocess', description: 'Reprocess the document if integrity failed' },
        ];
        return formatResponse(successResult(result));
    }
    catch (error) {
        return handleError(error);
    }
}
/**
 * Handle ocr_provenance_export - Export provenance data in various formats
 */
export async function handleProvenanceExport(params) {
    try {
        const input = validateInput(ProvenanceExportInput, params);
        const { db } = requireDatabase();
        // Collect provenance records based on scope
        let rawRecords = [];
        if (input.scope === 'document') {
            if (!input.document_id) {
                throw validationError('document_id is required when scope is "document"');
            }
            const doc = db.getDocument(input.document_id);
            if (!doc) {
                throw documentNotFoundError(input.document_id);
            }
            rawRecords = db.getProvenanceByRootDocument(doc.provenance_id);
        }
        else {
            const docs = db.listDocuments({ limit: 10000 });
            for (const doc of docs) {
                rawRecords.push(...db.getProvenanceByRootDocument(doc.provenance_id));
            }
        }
        // Filter null records and deduplicate by provenance ID (L-17: cross-document VLM dedup can overlap)
        const nonNullRecords = rawRecords.filter((r) => r !== null);
        const allRecords = [...new Map(nonNullRecords.map((r) => [r.id, r])).values()];
        // Summary-only mode: return counts without record data
        if (input.summary_only) {
            const byType = {};
            for (const r of allRecords) {
                byType[r.type] = (byType[r.type] || 0) + 1;
            }
            return formatResponse(successResult({
                scope: input.scope,
                format: input.format,
                document_id: input.document_id,
                total_records: allRecords.length,
                type_distribution: byType,
                next_steps: [
                    { tool: 'ocr_provenance_export', description: 'Get full records (summary_only=false)' },
                    { tool: 'ocr_provenance_query', description: 'Use filters for targeted export' },
                ],
            }));
        }
        // Apply pagination
        const expOffset = input.offset ?? 0;
        const expLimit = input.limit ?? 200;
        const totalRecords = allRecords.length;
        const records = allRecords.slice(expOffset, expOffset + expLimit);
        const hasMore = expOffset + expLimit < totalRecords;
        let data;
        if (input.format === 'json') {
            data = records.map((r) => ({
                id: r.id,
                type: r.type,
                chain_depth: r.chain_depth,
                processor: r.processor,
                processor_version: r.processor_version,
                content_hash: r.content_hash,
                parent_id: r.parent_id,
                root_document_id: r.root_document_id,
                created_at: r.created_at,
            }));
        }
        else if (input.format === 'w3c-prov') {
            // W3C PROV-JSON compliant export matching W3CProvDocument interface
            const prefix = {
                prov: 'http://www.w3.org/ns/prov#',
                ocr: 'http://ocr-provenance-mcp.local/ns#',
                xsd: 'http://www.w3.org/2001/XMLSchema#',
            };
            const entity = {};
            const activity = {};
            const agent = {
                'ocr:system': {
                    'prov:type': { $: 'ocr-provenance-mcp', type: 'xsd:string' },
                    'prov:label': 'OCR Provenance MCP System',
                },
            };
            const wasGeneratedBy = {};
            const wasDerivedFrom = {};
            const wasAttributedTo = {};
            const used = {};
            for (const r of records) {
                // Each provenance record is an entity
                entity[`ocr:${r.id}`] = {
                    'prov:type': { $: r.type, type: 'xsd:string' },
                    'ocr:contentHash': r.content_hash,
                    'ocr:chainDepth': r.chain_depth,
                    'prov:generatedAtTime': r.created_at,
                };
                // Only processing steps (chain_depth > 0) have activities
                if (r.chain_depth > 0) {
                    const activityId = `ocr:activity-${r.id}`;
                    activity[activityId] = {
                        'prov:type': { $: r.processor, type: 'xsd:string' },
                        'ocr:processorVersion': r.processor_version,
                        'prov:startedAtTime': r.created_at,
                    };
                    // wasGeneratedBy: entity was generated by activity
                    wasGeneratedBy[`ocr:wgb-${r.id}`] = {
                        'prov:entity': `ocr:${r.id}`,
                        'prov:activity': activityId,
                    };
                    if (r.parent_id) {
                        // wasDerivedFrom: child entity derived from parent entity
                        wasDerivedFrom[`ocr:wdf-${r.id}`] = {
                            'prov:generatedEntity': `ocr:${r.id}`,
                            'prov:usedEntity': `ocr:${r.parent_id}`,
                            'prov:activity': activityId,
                        };
                        // used: activity used parent entity as input
                        used[`ocr:used-${r.id}`] = {
                            'prov:activity': activityId,
                            'prov:entity': `ocr:${r.parent_id}`,
                        };
                    }
                }
                else {
                    // DOCUMENT entities (chain_depth 0) are attributed to the system agent
                    wasAttributedTo[`ocr:wat-${r.id}`] = {
                        'prov:entity': `ocr:${r.id}`,
                        'prov:agent': 'ocr:system',
                    };
                }
            }
            data = {
                prefix,
                entity,
                activity,
                agent,
                wasGeneratedBy,
                wasDerivedFrom,
                wasAttributedTo,
                used,
            };
        }
        else {
            // CSV format with proper escaping (M-10)
            const headers = [
                'id',
                'type',
                'chain_depth',
                'processor',
                'processor_version',
                'content_hash',
                'parent_id',
                'root_document_id',
                'created_at',
            ];
            const rows = records.map((r) => [
                csvEscape(r.id),
                csvEscape(r.type),
                csvEscape(r.chain_depth),
                csvEscape(r.processor),
                csvEscape(r.processor_version),
                csvEscape(r.content_hash),
                csvEscape(r.parent_id ?? ''),
                csvEscape(r.root_document_id),
                csvEscape(r.created_at),
            ].join(','));
            data = [headers.join(','), ...rows].join('\n');
        }
        const nextSteps = [];
        if (hasMore) {
            nextSteps.push({
                tool: 'ocr_provenance_export',
                description: `Get next page (offset=${expOffset + expLimit})`,
            });
        }
        nextSteps.push({ tool: 'ocr_provenance_query', description: 'Use filters for targeted export' });
        return formatResponse(successResult({
            scope: input.scope,
            format: input.format,
            document_id: input.document_id,
            total_records: totalRecords,
            returned: records.length,
            offset: expOffset,
            limit: expLimit,
            has_more: hasMore,
            data,
            next_steps: nextSteps,
        }));
    }
    catch (error) {
        return handleError(error);
    }
}
// ═══════════════════════════════════════════════════════════════════════════════
// PROVENANCE QUERY TOOL HANDLERS (Phase 5)
// ═══════════════════════════════════════════════════════════════════════════════
/**
 * Handle ocr_provenance_query - Query provenance records with filters
 *
 * Supports filtering by processor, type, chain_depth, date range, quality score,
 * duration, and root_document_id. Supports ordering and pagination.
 */
export async function handleProvenanceQuery(params) {
    try {
        const input = validateInput(z.object({
            processor: z.string().optional().describe('Filter by processor name'),
            type: z
                .enum([
                'DOCUMENT',
                'OCR_RESULT',
                'FORM_FILL',
                'CHUNK',
                'IMAGE',
                'EXTRACTION',
                'COMPARISON',
                'CLUSTERING',
                'EMBEDDING',
                'VLM_DESCRIPTION',
            ])
                .optional()
                .describe('Filter by provenance type'),
            chain_depth: z.number().int().min(0).optional().describe('Filter by exact chain depth'),
            created_after: z
                .string()
                .optional()
                .describe('Filter records created after this ISO 8601 timestamp'),
            created_before: z
                .string()
                .optional()
                .describe('Filter records created before this ISO 8601 timestamp'),
            min_quality_score: z
                .number()
                .min(0)
                .optional()
                .describe('Minimum processing quality score'),
            min_duration_ms: z.number().min(0).optional().describe('Minimum processing duration in ms'),
            root_document_id: z.string().optional().describe('Filter by root document provenance ID'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(100)
                .default(50)
                .describe('Maximum results (default 50)'),
            offset: z.number().int().min(0).default(0).describe('Offset for pagination'),
            order_by: z
                .enum(['created_at', 'processing_duration_ms', 'processing_quality_score'])
                .default('created_at')
                .describe('Field to order by'),
            order_dir: z.enum(['asc', 'desc']).default('desc').describe('Sort direction'),
        }), params);
        const { db } = requireDatabase();
        const { records, total } = db.queryProvenance({
            processor: input.processor,
            type: input.type,
            chain_depth: input.chain_depth,
            created_after: input.created_after,
            created_before: input.created_before,
            min_quality_score: input.min_quality_score,
            min_duration_ms: input.min_duration_ms,
            root_document_id: input.root_document_id,
            limit: input.limit,
            offset: input.offset,
            order_by: input.order_by,
            order_dir: input.order_dir,
        });
        // Build filters_applied for response transparency
        const filtersApplied = {};
        if (input.processor !== undefined)
            filtersApplied.processor = input.processor;
        if (input.type !== undefined)
            filtersApplied.type = input.type;
        if (input.chain_depth !== undefined)
            filtersApplied.chain_depth = input.chain_depth;
        if (input.created_after !== undefined)
            filtersApplied.created_after = input.created_after;
        if (input.created_before !== undefined)
            filtersApplied.created_before = input.created_before;
        if (input.min_quality_score !== undefined)
            filtersApplied.min_quality_score = input.min_quality_score;
        if (input.min_duration_ms !== undefined)
            filtersApplied.min_duration_ms = input.min_duration_ms;
        if (input.root_document_id !== undefined)
            filtersApplied.root_document_id = input.root_document_id;
        const formattedRecords = records.map((r) => ({
            id: r.id,
            type: r.type,
            chain_depth: r.chain_depth,
            processor: r.processor,
            processor_version: r.processor_version,
            processing_duration_ms: r.processing_duration_ms,
            processing_quality_score: r.processing_quality_score,
            content_hash: r.content_hash,
            root_document_id: r.root_document_id,
            parent_id: r.parent_id,
            created_at: r.created_at,
        }));
        return formatResponse(successResult({
            records: formattedRecords,
            total,
            limit: input.limit,
            offset: input.offset,
            filters_applied: filtersApplied,
            next_steps: [
                { tool: 'ocr_provenance_get', description: 'View full chain for a specific record' },
                { tool: 'ocr_document_get', description: 'Get document details for a provenance record' },
            ],
        }));
    }
    catch (error) {
        return handleError(error);
    }
}
/**
 * Handle ocr_provenance_timeline - Complete processing timeline for a document
 *
 * Shows every transformation chronologically with step numbers, types,
 * processors, durations, and quality scores.
 */
export async function handleProvenanceTimeline(params) {
    try {
        const input = validateInput(z.object({
            document_id: z.string().min(1).describe('Document ID to get timeline for'),
            include_params: z
                .boolean()
                .default(false)
                .describe('Include processing parameters in each step'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(200)
                .default(30)
                .describe('Maximum timeline entries to return (default 30)'),
            offset: z
                .number()
                .int()
                .min(0)
                .default(0)
                .describe('Number of entries to skip for pagination'),
        }), params);
        const { db } = requireDatabase();
        // Get the document to find its provenance_id
        const doc = db.getDocument(input.document_id);
        if (!doc) {
            throw documentNotFoundError(input.document_id);
        }
        // Get all provenance records for this document's root
        const records = db.getProvenanceByRootDocument(doc.provenance_id);
        if (records.length === 0) {
            return formatResponse(successResult({
                document_id: input.document_id,
                total_processing_time_ms: 0,
                steps_count: 0,
                timeline: [],
                next_steps: [
                    { tool: 'ocr_provenance_query', description: 'Query specific provenance records' },
                    { tool: 'ocr_trends', description: 'View processing volume trends (metric=volume)' },
                ],
            }));
        }
        // Sort chronologically by created_at, then by chain_depth for stable ordering
        const sorted = [...records].sort((a, b) => {
            const timeCompare = a.created_at.localeCompare(b.created_at);
            if (timeCompare !== 0)
                return timeCompare;
            return a.chain_depth - b.chain_depth;
        });
        // Compute total processing time
        let totalProcessingTimeMs = 0;
        for (const r of sorted) {
            if (r.processing_duration_ms !== null) {
                totalProcessingTimeMs += r.processing_duration_ms;
            }
        }
        // Compute type summary from full set
        const typeSummary = {};
        for (const r of sorted) {
            typeSummary[r.type] = (typeSummary[r.type] || 0) + 1;
        }
        // Apply pagination
        const tlOffset = input.offset ?? 0;
        const tlLimit = input.limit ?? 30;
        const totalSteps = sorted.length;
        const paginated = sorted.slice(tlOffset, tlOffset + tlLimit);
        const hasMore = tlOffset + tlLimit < totalSteps;
        // Build timeline entries
        const timeline = paginated.map((r, index) => {
            const entry = {
                step: tlOffset + index + 1,
                type: r.type,
                processor: r.processor,
                processor_version: r.processor_version,
                duration_ms: r.processing_duration_ms,
                quality_score: r.processing_quality_score,
                chain_depth: r.chain_depth,
                timestamp: r.created_at,
                provenance_id: r.id,
                parent_id: r.parent_id,
            };
            if (input.include_params) {
                entry.processing_params = r.processing_params;
            }
            return entry;
        });
        const nextSteps = [];
        if (hasMore) {
            nextSteps.push({
                tool: 'ocr_provenance_timeline',
                description: `Get next page (offset=${tlOffset + tlLimit})`,
            });
        }
        nextSteps.push({ tool: 'ocr_provenance_query', description: 'Query specific provenance records' }, { tool: 'ocr_document_get', description: 'Read the original document' });
        return formatResponse(successResult({
            document_id: input.document_id,
            total_processing_time_ms: totalProcessingTimeMs,
            steps_count: totalSteps,
            returned: timeline.length,
            offset: tlOffset,
            limit: tlLimit,
            has_more: hasMore,
            type_summary: typeSummary,
            timeline,
            next_steps: nextSteps,
        }));
    }
    catch (error) {
        return handleError(error);
    }
}
/**
 * Handle ocr_provenance_processor_stats - Aggregate statistics per processor
 *
 * Groups by processor and processor_version with COUNT, AVG, MIN, MAX, SUM
 * aggregations on processing_duration_ms and processing_quality_score.
 */
export async function handleProvenanceProcessorStats(params) {
    try {
        const input = validateInput(z.object({
            processor: z.string().optional().describe('Filter by specific processor name'),
            created_after: z
                .string()
                .optional()
                .describe('Filter records created after this ISO 8601 timestamp'),
            created_before: z
                .string()
                .optional()
                .describe('Filter records created before this ISO 8601 timestamp'),
        }), params);
        const { db } = requireDatabase();
        const stats = db.getProvenanceProcessorStats({
            processor: input.processor,
            created_after: input.created_after,
            created_before: input.created_before,
        });
        return formatResponse(successResult({
            stats,
            total_processors: stats.length,
            next_steps: [
                {
                    tool: 'ocr_report_performance',
                    description: 'Get detailed pipeline performance analytics',
                },
                { tool: 'ocr_provenance_query', description: 'Query provenance by processor' },
            ],
        }));
    }
    catch (error) {
        return handleError(error);
    }
}
// ═══════════════════════════════════════════════════════════════════════════════
// TOOL DEFINITIONS FOR MCP REGISTRATION
// ═══════════════════════════════════════════════════════════════════════════════
/**
 * Provenance tools collection for MCP server registration
 */
export const provenanceTools = {
    ocr_provenance_get: {
        description: '[ANALYSIS] Returns provenance chain with descendant summary. Use include_descendants=true for full records. To read the original document, follow up with ocr_document_get.',
        inputSchema: {
            item_id: z
                .string()
                .min(1)
                .describe('ID of the item (document, ocr_result, chunk, embedding, image, comparison, clustering, form_fill, extraction, or provenance)'),
            item_type: z
                .enum([
                'document',
                'ocr_result',
                'chunk',
                'embedding',
                'image',
                'comparison',
                'clustering',
                'form_fill',
                'extraction',
                'auto',
            ])
                .default('auto')
                .describe('Type of item'),
            include_descendants: z
                .boolean()
                .default(false)
                .describe('Set true to get individual descendant records. Default returns only a count summary by type.'),
            descendants_limit: z
                .number()
                .int()
                .min(1)
                .max(200)
                .default(50)
                .describe('Max descendant records to return when include_descendants=true (default 50)'),
        },
        handler: handleProvenanceGet,
    },
    ocr_provenance_verify: {
        description: '[ANALYSIS] Use to verify data integrity by checking content hashes and chain consistency. Returns verification status with any issues found.',
        inputSchema: {
            item_id: z.string().min(1).describe('ID of the item to verify'),
            verify_content: z.boolean().default(true).describe('Verify content hashes'),
            verify_chain: z.boolean().default(true).describe('Verify chain integrity'),
        },
        handler: handleProvenanceVerify,
    },
    ocr_provenance_export: {
        description: '[STATUS] Export provenance records to JSON, W3C PROV-JSON, or CSV. Paginated (default 200). Use summary_only=true for record counts without data.',
        inputSchema: {
            scope: z.enum(['document', 'database']).describe('Export scope'),
            document_id: z.string().optional().describe('Document ID (required when scope is document)'),
            format: z.enum(['json', 'w3c-prov', 'csv']).default('json').describe('Export format'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(1000)
                .default(200)
                .describe('Maximum records to return (default 200)'),
            offset: z
                .number()
                .int()
                .min(0)
                .default(0)
                .describe('Number of records to skip for pagination'),
            summary_only: z
                .boolean()
                .default(false)
                .describe('When true, return only record count and type distribution'),
        },
        handler: handleProvenanceExport,
    },
    ocr_provenance_query: {
        description: '[ANALYSIS] Use to query provenance records with filters (processor, type, date, depth, quality). Returns paginated records for auditing.',
        inputSchema: {
            processor: z.string().optional().describe('Filter by processor name'),
            type: z
                .enum([
                'DOCUMENT',
                'OCR_RESULT',
                'FORM_FILL',
                'CHUNK',
                'IMAGE',
                'EXTRACTION',
                'COMPARISON',
                'CLUSTERING',
                'EMBEDDING',
                'VLM_DESCRIPTION',
            ])
                .optional()
                .describe('Filter by provenance type'),
            chain_depth: z.number().int().min(0).optional().describe('Filter by exact chain depth'),
            created_after: z
                .string()
                .optional()
                .describe('Filter records created after this ISO 8601 timestamp'),
            created_before: z
                .string()
                .optional()
                .describe('Filter records created before this ISO 8601 timestamp'),
            min_quality_score: z.number().min(0).optional().describe('Minimum processing quality score'),
            min_duration_ms: z.number().min(0).optional().describe('Minimum processing duration in ms'),
            root_document_id: z.string().optional().describe('Filter by root document provenance ID'),
            limit: z.number().int().min(1).max(100).default(50).describe('Maximum results (default 50)'),
            offset: z.number().int().min(0).default(0).describe('Offset for pagination'),
            order_by: z
                .enum(['created_at', 'processing_duration_ms', 'processing_quality_score'])
                .default('created_at')
                .describe('Field to order by'),
            order_dir: z.enum(['asc', 'desc']).default('desc').describe('Sort direction'),
        },
        handler: handleProvenanceQuery,
    },
    ocr_provenance_timeline: {
        description: '[ANALYSIS] Processing timeline for a document. Paginated (default 30). Returns type_summary and chronological steps with durations.',
        inputSchema: {
            document_id: z.string().min(1).describe('Document ID to get timeline for'),
            include_params: z
                .boolean()
                .default(false)
                .describe('Include processing parameters in each step'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(200)
                .default(30)
                .describe('Maximum timeline entries to return (default 30)'),
            offset: z
                .number()
                .int()
                .min(0)
                .default(0)
                .describe('Number of entries to skip for pagination'),
        },
        handler: handleProvenanceTimeline,
    },
    ocr_provenance_processor_stats: {
        description: '[STATUS] Use to get aggregate performance stats per processor (operation counts, durations, quality). Returns processor-level analytics.',
        inputSchema: {
            processor: z.string().optional().describe('Filter by specific processor name'),
            created_after: z
                .string()
                .optional()
                .describe('Filter records created after this ISO 8601 timestamp'),
            created_before: z
                .string()
                .optional()
                .describe('Filter records created before this ISO 8601 timestamp'),
        },
        handler: handleProvenanceProcessorStats,
    },
};
//# sourceMappingURL=provenance.js.map