/**
 * Session Utilities
 *
 * Shared utilities for tmux session naming, parsing, and discovery.
 * Used by session/list.ts, session/attach.ts, session/health.ts, and session/peek.ts commands.
 */
import { execSync } from 'node:child_process';
/**
 * Capture the last N lines from a tmux pane.
 * Supports both host tmux sessions and container tmux sessions (via docker exec).
 *
 * @param sessionId - The tmux session ID to capture from
 * @param lines - Number of scrollback lines to capture
 * @param containerId - Optional container ID for container-based sessions
 * @returns The captured pane content, or null if capture fails
 */
export function captureTmuxPane(sessionId, lines, containerId) {
    try {
        const captureCmd = `tmux capture-pane -t "${sessionId}" -p -S -${lines}`;
        if (containerId) {
            return execSync(`docker exec ${containerId} bash -c '${captureCmd}'`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 }).trim();
        }
        return execSync(captureCmd, {
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'pipe'],
            timeout: 5000,
        }).trim();
    }
    catch {
        return null;
    }
}
/**
 * Known action names used in session naming.
 * These are the actions defined in pmo/actions/ that may be used when spawning agents.
 */
export const KNOWN_ACTIONS = [
    'Implement',
    'Review',
    'Fix',
    'Refactor',
    'Test',
    'Document',
    'work', // Default fallback
];
/**
 * Parse a tmux session name following prlt naming convention.
 * Format: {ticketId}-{action}-{agentName}
 *
 * Note: Agent names can contain hyphens (like "stout-page"), so we match
 * from the end using known action names to correctly split the components.
 *
 * Example: "TKT-878-Implement-stout-page" -> { ticketId: "TKT-878", action: "Implement", agentName: "stout-page" }
 */
export function parseSessionName(sessionName) {
    // First, extract the ticket ID (format: TKT-### or PROJECT-###)
    const ticketMatch = sessionName.match(/^(TKT-\d+|[A-Z]+-\d+)-/);
    if (!ticketMatch) {
        return null;
    }
    const ticketId = ticketMatch[1];
    const remainder = sessionName.slice(ticketMatch[0].length);
    // Try to match known actions (case-insensitive) at the start of the remainder
    for (const action of KNOWN_ACTIONS) {
        const actionLower = action.toLowerCase();
        const remainderLower = remainder.toLowerCase();
        // Check if remainder starts with this action followed by a hyphen
        if (remainderLower.startsWith(actionLower + '-')) {
            const agentName = remainder.slice(action.length + 1);
            if (agentName) {
                return {
                    ticketId,
                    action: remainder.slice(0, action.length), // Preserve original casing
                    agentName,
                };
            }
        }
    }
    // Fallback: Split on first hyphen (original behavior for unknown actions)
    const parts = remainder.split('-');
    if (parts.length >= 2) {
        return {
            ticketId,
            action: parts[0],
            agentName: parts.slice(1).join('-'),
        };
    }
    return null;
}
/**
 * Build expected session name from execution data.
 * Format: {ticketId}-{action}-{agentName}
 * This is the same format used by runners.ts buildSessionName()
 */
export function buildExpectedSessionName(ticketId, agentName, action = 'work') {
    return `${ticketId}-${action}-${agentName}`;
}
/**
 * Check if a session name matches the expected pattern for a ticket and agent.
 * Parses the session name and verifies the ticket ID and agent name match exactly.
 */
export function sessionMatchesExecution(sessionName, ticketId, agentName) {
    const parsed = parseSessionName(sessionName);
    if (!parsed)
        return false;
    return parsed.ticketId === ticketId && parsed.agentName === agentName;
}
/**
 * Get list of host tmux session names.
 */
export function getHostTmuxSessionNames() {
    try {
        execSync('which tmux', { stdio: 'pipe' });
        const output = execSync('tmux list-sessions -F "#{session_name}"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
        if (!output)
            return [];
        return output.split('\n');
    }
    catch {
        return [];
    }
}
/**
 * Get map of containerId -> tmux session names.
 * Only checks containers with the devcontainer.local_folder label.
 */
export function getContainerTmuxSessionMap() {
    const sessionMap = new Map();
    try {
        const containerIds = new Set();
        // Primary: devcontainer-labeled containers (historical behavior).
        try {
            const labeledOutput = execSync('docker ps --filter "label=devcontainer.local_folder" --format "{{.ID}}"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
            if (labeledOutput) {
                for (const id of labeledOutput.split('\n')) {
                    if (id.trim())
                        containerIds.add(id.trim());
                }
            }
        }
        catch {
            // Ignore and continue with broad fallback discovery.
        }
        // Fallback: include all running containers so prlt-managed devcontainers
        // without the devcontainer.local_folder label are still discoverable.
        try {
            const allOutput = execSync('docker ps --format "{{.ID}}"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
            if (allOutput) {
                for (const id of allOutput.split('\n')) {
                    if (id.trim())
                        containerIds.add(id.trim());
                }
            }
        }
        catch {
            // Docker not available.
        }
        if (containerIds.size === 0)
            return sessionMap;
        for (const containerId of containerIds) {
            try {
                const tmuxOutput = execSync(`docker exec ${containerId} tmux list-sessions -F "#{session_name}" 2>/dev/null`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
                if (tmuxOutput) {
                    sessionMap.set(containerId, tmuxOutput.split('\n'));
                }
            }
            catch {
                // Container has no tmux sessions
            }
        }
    }
    catch {
        // Docker not available
    }
    return sessionMap;
}
/**
 * Flatten container sessions map into an array for easier iteration.
 */
export function flattenContainerSessions(containerTmuxSessions) {
    const result = [];
    containerTmuxSessions.forEach((sessions, containerId) => {
        for (const sessionName of sessions) {
            result.push({ sessionName, containerId });
        }
    });
    return result;
}
/**
 * Find container sessions using prefix matching.
 * Handles short vs full container ID mismatches between DB records and docker output.
 */
export function findContainerSessionsByPrefix(containerTmuxSessions, containerId) {
    const exact = containerTmuxSessions.get(containerId);
    if (exact)
        return exact;
    for (const [key, sessions] of containerTmuxSessions) {
        if (key.startsWith(containerId) || containerId.startsWith(key)) {
            return sessions;
        }
    }
    return [];
}
/**
 * Try to find a matching tmux session for an execution with NULL sessionId.
 * First tries exact matches with known action names, then falls back to
 * partial matching with agent name verification.
 *
 * @returns The matched session name, or null if no match found
 */
export function findSessionForExecution(ticketId, agentName, availableSessions) {
    // First, try exact matches with known action names
    for (const action of KNOWN_ACTIONS) {
        const expectedName = buildExpectedSessionName(ticketId, agentName, action);
        if (availableSessions.includes(expectedName)) {
            return expectedName;
        }
        // Also try lowercase variant
        const expectedNameLower = buildExpectedSessionName(ticketId, agentName, action.toLowerCase());
        if (availableSessions.includes(expectedNameLower)) {
            return expectedNameLower;
        }
    }
    // Fallback: partial match with agent name verification
    // This catches sessions with unknown action names while preventing
    // false matches when multiple agents work on the same ticket
    const match = availableSessions.find(s => sessionMatchesExecution(s, ticketId, agentName));
    return match || null;
}
//# sourceMappingURL=session-utils.js.map