/**
 * Playbook Operations for DatabaseService
 *
 * Provides CRUD operations for the playbooks table and clause comparison.
 * Playbooks define preferred contract terms for deviation detection.
 *
 * @module database/playbook-operations
 */
import { v4 as uuidv4 } from 'uuid';
// =============================================================================
// CREATE
// =============================================================================
/**
 * Create a new playbook with clauses
 */
export function createPlaybook(db, params) {
    const id = uuidv4();
    const now = new Date().toISOString();
    const clausesJson = JSON.stringify(params.clauses);
    db.prepare(`
    INSERT INTO playbooks (id, name, description, clauses_json, created_at, updated_at, created_by)
    VALUES (?, ?, ?, ?, ?, ?, ?)
  `).run(id, params.name, params.description ?? null, clausesJson, now, now, params.created_by ?? null);
    return {
        id,
        name: params.name,
        description: params.description ?? null,
        clauses: params.clauses,
        created_at: now,
        updated_at: now,
        created_by: params.created_by ?? null,
    };
}
// =============================================================================
// GET
// =============================================================================
/**
 * Get a playbook by ID
 * @throws Error if not found
 */
export function getPlaybook(db, id) {
    const row = db.prepare('SELECT * FROM playbooks WHERE id = ?').get(id);
    if (!row) {
        throw new Error(`Playbook not found: ${id}`);
    }
    let clauses = [];
    try {
        clauses = JSON.parse(row.clauses_json);
    }
    catch (error) {
        throw new Error(`Corrupt clauses_json in playbook ${row.id}: ${error instanceof Error ? error.message : String(error)}`);
    }
    return {
        id: row.id,
        name: row.name,
        description: row.description,
        clauses,
        created_at: row.created_at,
        updated_at: row.updated_at,
        created_by: row.created_by,
    };
}
// =============================================================================
// LIST
// =============================================================================
/**
 * List all playbooks
 */
export function listPlaybooks(db) {
    const rows = db
        .prepare('SELECT * FROM playbooks ORDER BY updated_at DESC LIMIT 1000')
        .all();
    return rows.map((row) => {
        let clauses = [];
        try {
            clauses = JSON.parse(row.clauses_json);
        }
        catch (error) {
            throw new Error(`Corrupt clauses_json in playbook ${row.id}: ${error instanceof Error ? error.message : String(error)}`);
        }
        return {
            id: row.id,
            name: row.name,
            description: row.description,
            clauses,
            created_at: row.created_at,
            updated_at: row.updated_at,
            created_by: row.created_by,
        };
    });
}
// =============================================================================
// COMPARE WITH PLAYBOOK
// =============================================================================
/**
 * Compare document text against a playbook's clauses.
 *
 * For each clause in the playbook, searches the document's chunks for matching
 * content using simple text matching (case-insensitive indexOf). Returns
 * deviations where neither preferred text nor alternatives are found.
 */
export function compareWithPlaybook(db, documentId, playbookId) {
    // Verify document exists
    const doc = db.prepare('SELECT id FROM documents WHERE id = ?').get(documentId);
    if (!doc) {
        throw new Error(`Document not found: ${documentId}`);
    }
    // Get playbook
    const playbook = getPlaybook(db, playbookId);
    // Get all chunks for the document
    const chunks = db
        .prepare(`
    SELECT c.id, c.text, c.page_number
    FROM chunks c
    INNER JOIN ocr_results o ON o.id = c.ocr_result_id
    WHERE o.document_id = ?
    ORDER BY c.chunk_index ASC
  `)
        .all(documentId);
    const clauseResults = [];
    let matches = 0;
    let alternativeMatches = 0;
    let deviations = 0;
    let missing = 0;
    for (const clause of playbook.clauses) {
        const result = matchClauseInChunks(clause, chunks);
        clauseResults.push(result);
        switch (result.status) {
            case 'match':
                matches++;
                break;
            case 'alternative_match':
                alternativeMatches++;
                break;
            case 'deviation':
                deviations++;
                break;
            case 'missing':
                missing++;
                break;
        }
    }
    const totalClauses = playbook.clauses.length;
    const complianceScore = totalClauses > 0
        ? Math.round(((matches + alternativeMatches) / totalClauses) * 100) / 100
        : 1.0;
    return {
        playbook_id: playbook.id,
        playbook_name: playbook.name,
        document_id: documentId,
        total_clauses: totalClauses,
        matches,
        alternative_matches: alternativeMatches,
        deviations,
        missing,
        compliance_score: complianceScore,
        clause_results: clauseResults,
    };
}
// =============================================================================
// HELPERS
// =============================================================================
/**
 * Search chunks for a clause match using case-insensitive text matching.
 * Checks preferred text first, then alternatives.
 */
function matchClauseInChunks(clause, chunks) {
    const preferredLower = clause.preferred_text.toLowerCase();
    // Check preferred text first
    for (const chunk of chunks) {
        const textLower = chunk.text.toLowerCase();
        if (textLower.includes(preferredLower)) {
            return {
                clause_name: clause.clause_name,
                severity: clause.severity,
                status: 'match',
                preferred_text: clause.preferred_text,
                matched_text: extractContext(chunk.text, clause.preferred_text),
                matched_chunk_id: chunk.id,
                matched_page: chunk.page_number,
                details: 'Preferred text found in document',
            };
        }
    }
    // Check alternatives
    for (const alt of clause.alternatives) {
        const altLower = alt.toLowerCase();
        for (const chunk of chunks) {
            const textLower = chunk.text.toLowerCase();
            if (textLower.includes(altLower)) {
                return {
                    clause_name: clause.clause_name,
                    severity: clause.severity,
                    status: 'alternative_match',
                    preferred_text: clause.preferred_text,
                    matched_text: extractContext(chunk.text, alt),
                    matched_chunk_id: chunk.id,
                    matched_page: chunk.page_number,
                    details: `Alternative text found: "${alt}"`,
                };
            }
        }
    }
    // Check if clause topic is mentioned at all (by clause name)
    const clauseNameLower = clause.clause_name.toLowerCase();
    for (const chunk of chunks) {
        const textLower = chunk.text.toLowerCase();
        if (textLower.includes(clauseNameLower)) {
            return {
                clause_name: clause.clause_name,
                severity: clause.severity,
                status: 'deviation',
                preferred_text: clause.preferred_text,
                matched_text: extractContext(chunk.text, clause.clause_name),
                matched_chunk_id: chunk.id,
                matched_page: chunk.page_number,
                details: `Clause topic "${clause.clause_name}" found but preferred/alternative text not matched`,
            };
        }
    }
    // Not found at all
    return {
        clause_name: clause.clause_name,
        severity: clause.severity,
        status: 'missing',
        preferred_text: clause.preferred_text,
        matched_text: null,
        matched_chunk_id: null,
        matched_page: null,
        details: `Clause "${clause.clause_name}" not found in document`,
    };
}
/**
 * Extract context around a matched substring (up to 200 chars on each side)
 */
function extractContext(fullText, searchText) {
    const lowerFull = fullText.toLowerCase();
    const lowerSearch = searchText.toLowerCase();
    const idx = lowerFull.indexOf(lowerSearch);
    if (idx === -1)
        return fullText.substring(0, 400);
    const start = Math.max(0, idx - 100);
    const end = Math.min(fullText.length, idx + searchText.length + 100);
    let context = fullText.substring(start, end);
    if (start > 0)
        context = '...' + context;
    if (end < fullText.length)
        context = context + '...';
    return context;
}
//# sourceMappingURL=playbook-operations.js.map