"use strict";
/**
 * Chat message database operations
 * CRUD operations for chat_messages table
 *
 * Issue #479: Extracted from db.ts for single-responsibility separation
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ACTIVE_FILTER = void 0;
exports.getLastAssistantMessageAt = getLastAssistantMessageAt;
exports.createMessage = createMessage;
exports.updateMessageContent = updateMessageContent;
exports.getMessages = getMessages;
exports.getLastUserMessage = getLastUserMessage;
exports.getLastMessage = getLastMessage;
exports.deleteAllMessages = deleteAllMessages;
exports.deleteMessageById = deleteMessageById;
exports.deleteMessagesByCliTool = deleteMessagesByCliTool;
exports.updateLastUserMessage = updateLastUserMessage;
exports.clearLastUserMessage = clearLastUserMessage;
exports.getMessageById = getMessageById;
exports.updatePromptData = updatePromptData;
exports.markPendingPromptsAsAnswered = markPendingPromptsAsAnswered;
const crypto_1 = require("crypto");
const logger_1 = require("../../lib/logger");
const logger = (0, logger_1.createLogger)('chat-db');
/** Active (non-archived) message filter clause. Single point of change for archived filtering. */
exports.ACTIVE_FILTER = 'AND archived = 0';
function mapChatMessage(row) {
    return {
        id: row.id,
        worktreeId: row.worktree_id,
        role: row.role,
        content: row.content,
        summary: row.summary || undefined,
        timestamp: new Date(row.timestamp),
        logFileName: row.log_file_name || undefined,
        requestId: row.request_id || undefined,
        messageType: row.message_type || 'normal',
        promptData: row.prompt_data ? JSON.parse(row.prompt_data) : undefined,
        cliToolId: row.cli_tool_id ?? 'claude',
        archived: row.archived === 1,
    };
}
/**
 * Update worktree's updated_at timestamp
 * @private
 */
function updateWorktreeTimestamp(db, worktreeId, timestamp) {
    const stmt = db.prepare(`
    UPDATE worktrees
    SET updated_at = ?
    WHERE id = ?
  `);
    stmt.run(timestamp.getTime(), worktreeId);
}
/**
 * Get the timestamp of the most recent assistant message for a worktree
 * Used for unread tracking (Issue #31)
 */
function getLastAssistantMessageAt(db, worktreeId) {
    const stmt = db.prepare(`
    SELECT MAX(timestamp) as last_assistant_message_at
    FROM chat_messages
    WHERE worktree_id = ? AND role = 'assistant' ${exports.ACTIVE_FILTER}
  `);
    const row = stmt.get(worktreeId);
    if (!row || row.last_assistant_message_at === null) {
        return null;
    }
    return new Date(row.last_assistant_message_at);
}
/**
 * Create a new chat message
 */
function createMessage(db, message) {
    const id = (0, crypto_1.randomUUID)();
    const stmt = db.prepare(`
    INSERT INTO chat_messages
    (id, worktree_id, role, content, summary, timestamp, log_file_name, request_id, message_type, prompt_data, cli_tool_id)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `);
    stmt.run(id, message.worktreeId, message.role, message.content, message.summary || null, message.timestamp.getTime(), message.logFileName || null, message.requestId || null, message.messageType || 'normal', message.promptData ? JSON.stringify(message.promptData) : null, message.cliToolId || 'claude');
    // Update worktree's updated_at timestamp
    updateWorktreeTimestamp(db, message.worktreeId, message.timestamp);
    // If this is a user message, update last_user_message
    if (message.role === 'user') {
        updateLastUserMessage(db, message.worktreeId, message.content, message.timestamp);
    }
    return { id, ...message, archived: false };
}
function updateMessageContent(db, messageId, content, options) {
    const assignments = ['content = ?'];
    const params = [content];
    if (options?.summary !== undefined) {
        assignments.push('summary = ?');
        params.push(options.summary ?? null);
    }
    if (options?.logFileName !== undefined) {
        assignments.push('log_file_name = ?');
        params.push(options.logFileName ?? null);
    }
    if (options?.requestId !== undefined) {
        assignments.push('request_id = ?');
        params.push(options.requestId ?? null);
    }
    const stmt = db.prepare(`
    UPDATE chat_messages
    SET ${assignments.join(', ')}
    WHERE id = ?
  `);
    stmt.run(...params, messageId);
}
/**
 * Get messages for a worktree, optionally filtered by CLI tool
 */
function getMessages(db, worktreeId, options = {}) {
    const { before, limit = 50, cliToolId, includeArchived = false } = options;
    let query = `
    SELECT id, worktree_id, role, content, summary, timestamp, log_file_name, request_id, message_type, prompt_data, cli_tool_id, archived
    FROM chat_messages
    WHERE worktree_id = ? AND (? IS NULL OR timestamp < ?)
  `;
    const params = [worktreeId, before?.getTime() || null, before?.getTime() || null];
    // archived filter (default: non-archived only)
    if (!includeArchived) {
        query += ` ${exports.ACTIVE_FILTER}`;
    }
    // Add CLI tool filter if specified
    if (cliToolId) {
        query += ` AND cli_tool_id = ?`;
        params.push(cliToolId);
    }
    query += ` ORDER BY timestamp DESC LIMIT ?`;
    params.push(limit);
    const stmt = db.prepare(query);
    const rows = stmt.all(...params);
    return rows.map(mapChatMessage);
}
/**
 * Fetch the most recent user-authored message for a worktree.
 */
function getLastUserMessage(db, worktreeId) {
    const stmt = db.prepare(`
    SELECT id, worktree_id, role, content, summary, timestamp, log_file_name, request_id, message_type, prompt_data, cli_tool_id, archived
    FROM chat_messages
    WHERE worktree_id = ? AND role = 'user' ${exports.ACTIVE_FILTER}
    ORDER BY timestamp DESC
    LIMIT 1
  `);
    const row = stmt.get(worktreeId);
    return row ? mapChatMessage(row) : null;
}
/**
 * Fetch the most recent message for a worktree (any role).
 * Used to determine if waiting for Claude's response.
 */
function getLastMessage(db, worktreeId) {
    const stmt = db.prepare(`
    SELECT id, worktree_id, role, content, summary, timestamp, log_file_name, request_id, message_type, prompt_data, cli_tool_id, archived
    FROM chat_messages
    WHERE worktree_id = ? ${exports.ACTIVE_FILTER}
    ORDER BY timestamp DESC
    LIMIT 1
  `);
    const row = stmt.get(worktreeId);
    return row ? mapChatMessage(row) : null;
}
/**
 * Archive all active messages for a worktree (logical deletion)
 * Used when killing a session to clear message history while retaining for future viewing.
 * Note: Log files are preserved for historical reference
 *
 * Issue #168: Changed from physical DELETE to logical UPDATE (archived=1)
 * Function name maintained for backward compatibility (DR1-003)
 *
 * @returns Number of archived messages
 */
function deleteAllMessages(db, worktreeId) {
    const stmt = db.prepare(`
    UPDATE chat_messages
    SET archived = 1
    WHERE worktree_id = ? ${exports.ACTIVE_FILTER}
  `);
    const result = stmt.run(worktreeId);
    logger.info('archived-all-messages', { worktreeId, count: result.changes });
    return result.changes;
}
/**
 * Delete a single message by its ID
 * Used to clean up orphaned user messages (e.g., when a user re-sends a message
 * after the previous one received no response).
 *
 * @param db - Database instance
 * @param messageId - ID of the message to delete
 * @returns True if a message was deleted, false otherwise
 */
function deleteMessageById(db, messageId) {
    const stmt = db.prepare(`
    DELETE FROM chat_messages
    WHERE id = ?
  `);
    const result = stmt.run(messageId);
    return result.changes > 0;
}
/**
 * Archive messages for a specific CLI tool in a worktree (logical deletion)
 * Issue #4: T4.2 - Individual CLI tool session termination (MF3-001)
 * Issue #168: Changed from physical DELETE to logical UPDATE (archived=1)
 *
 * Used when killing only a specific CLI tool's session to archive its message history
 * while preserving messages from other CLI tools.
 * Note: Log files are preserved for historical reference
 * Function name maintained for backward compatibility (DR1-003)
 *
 * @param db - Database instance
 * @param worktreeId - Worktree ID
 * @param cliTool - CLI tool ID to archive messages for
 * @returns Number of archived messages
 */
function deleteMessagesByCliTool(db, worktreeId, cliTool) {
    const stmt = db.prepare(`
    UPDATE chat_messages
    SET archived = 1
    WHERE worktree_id = ? AND cli_tool_id = ? ${exports.ACTIVE_FILTER}
  `);
    const result = stmt.run(worktreeId, cliTool);
    logger.info('archived-messages-by-cli-tool', { worktreeId, cliTool, count: result.changes });
    return result.changes;
}
/**
 * Update worktree's last user message
 */
function updateLastUserMessage(db, worktreeId, message, timestamp) {
    const stmt = db.prepare(`
    UPDATE worktrees
    SET last_user_message = ?,
        last_user_message_at = ?
    WHERE id = ?
  `);
    // Truncate message to 200 characters
    const truncatedMessage = message.substring(0, 200);
    stmt.run(truncatedMessage, timestamp.getTime(), worktreeId);
}
/**
 * Clear worktree's last user message fields
 * Issue #168: Called after archiving messages to prevent stale data in sidebar
 */
function clearLastUserMessage(db, worktreeId) {
    const stmt = db.prepare(`
    UPDATE worktrees
    SET last_user_message = NULL,
        last_user_message_at = NULL
    WHERE id = ?
  `);
    stmt.run(worktreeId);
}
/**
 * Get message by ID
 */
function getMessageById(db, messageId) {
    const stmt = db.prepare(`
    SELECT id, worktree_id, role, content, summary, timestamp, log_file_name, request_id, message_type, prompt_data, cli_tool_id, archived
    FROM chat_messages
    WHERE id = ?
  `);
    const row = stmt.get(messageId);
    if (!row) {
        return null;
    }
    return mapChatMessage(row);
}
/**
 * Update prompt data for a message
 */
function updatePromptData(db, messageId, promptData) {
    const stmt = db.prepare(`
    UPDATE chat_messages
    SET prompt_data = ?
    WHERE id = ?
  `);
    stmt.run(JSON.stringify(promptData), messageId);
}
/**
 * Mark all pending prompts as answered for a worktree/CLI tool
 * This is called when we detect Claude has started processing (new response detected)
 * which means any pending prompts must have been answered via terminal
 */
function markPendingPromptsAsAnswered(db, worktreeId, cliToolId) {
    // Find all pending prompt messages for this worktree/CLI tool
    const selectStmt = db.prepare(`
    SELECT id, prompt_data
    FROM chat_messages
    WHERE worktree_id = ?
      AND cli_tool_id = ?
      AND message_type = 'prompt'
      AND json_extract(prompt_data, '$.status') = 'pending'
      ${exports.ACTIVE_FILTER}
    ORDER BY timestamp DESC
  `);
    const rows = selectStmt.all(worktreeId, cliToolId);
    if (rows.length === 0) {
        return 0;
    }
    // Update each pending prompt to answered
    const updateStmt = db.prepare(`
    UPDATE chat_messages
    SET prompt_data = ?
    WHERE id = ?
  `);
    let updatedCount = 0;
    for (const row of rows) {
        try {
            const promptData = JSON.parse(row.prompt_data);
            promptData.status = 'answered';
            promptData.answer = '(answered via terminal)';
            promptData.answeredAt = new Date().toISOString();
            updateStmt.run(JSON.stringify(promptData), row.id);
            updatedCount++;
        }
        catch {
            // Skip if prompt_data is invalid JSON
        }
    }
    return updatedCount;
}
