/**
 * Commands Routes Module
 * Handles all Commands-related API endpoints
 *
 * API Endpoints:
 * - GET /api/commands - List all commands with groups
 * - POST /api/commands/:name/toggle - Enable/disable single command
 * - POST /api/commands/group/:groupName/toggle - Batch toggle commands by group
 */
import { existsSync, readdirSync, readFileSync, mkdirSync, renameSync } from 'fs';
import { join, relative, dirname, basename } from 'path';
import { homedir } from 'os';
import { validatePath as validateAllowedPath } from '../../utils/path-validator.js';
// ========== Helper Functions ==========
function isRecord(value) {
    return typeof value === 'object' && value !== null;
}
/**
 * Get commands directory path
 */
function getCommandsDir(location, projectPath) {
    if (location === 'project') {
        return join(projectPath, '.claude', 'commands');
    }
    return join(homedir(), '.claude', 'commands');
}
/**
 * Parse YAML frontmatter from command file
 */
function parseCommandFrontmatter(content) {
    const result = {
        name: '',
        description: '',
        group: 'other' // Default group
    };
    // Check for YAML frontmatter
    if (content.startsWith('---')) {
        const endIndex = content.indexOf('---', 3);
        if (endIndex > 0) {
            const frontmatter = content.substring(3, endIndex).trim();
            // Parse frontmatter lines
            const lines = frontmatter.split(/[\r\n]+/);
            for (const line of lines) {
                const colonIndex = line.indexOf(':');
                if (colonIndex > 0) {
                    const key = line.substring(0, colonIndex).trim().toLowerCase();
                    const value = line.substring(colonIndex + 1).trim().replace(/^["']|["']$/g, '');
                    if (key === 'name') {
                        result.name = value;
                    }
                    else if (key === 'description') {
                        result.description = value;
                    }
                    else if (key === 'group') {
                        result.group = value || 'other';
                    }
                    else if (key === 'argument-hint') {
                        result.argumentHint = value;
                    }
                    else if (key === 'allowed-tools') {
                        result.allowedTools = value
                            .replace(/^\[|\]$/g, '')
                            .split(',')
                            .map(t => t.trim())
                            .filter(Boolean);
                    }
                }
            }
        }
    }
    return result;
}
/**
 * Get command groups config file path
 */
function getGroupsConfigPath(location, projectPath) {
    const baseDir = location === 'project'
        ? join(projectPath, '.claude')
        : join(homedir(), '.claude');
    return join(baseDir, 'command-groups.json');
}
/**
 * Load command groups configuration
 */
function loadGroupsConfig(location, projectPath) {
    const configPath = getGroupsConfigPath(location, projectPath);
    const defaultConfig = {
        groups: {},
        assignments: {}
    };
    if (!existsSync(configPath)) {
        return defaultConfig;
    }
    try {
        const content = readFileSync(configPath, 'utf8');
        const parsed = JSON.parse(content);
        return {
            groups: isRecord(parsed.groups) ? parsed.groups : {},
            assignments: isRecord(parsed.assignments) ? parsed.assignments : {}
        };
    }
    catch (err) {
        console.error(`[Commands] Failed to load groups config from ${configPath}:`, err);
        return defaultConfig;
    }
}
/**
 * Save command groups configuration
 */
function saveGroupsConfig(location, projectPath, config) {
    const configPath = getGroupsConfigPath(location, projectPath);
    const configDir = dirname(configPath);
    if (!existsSync(configDir)) {
        mkdirSync(configDir, { recursive: true });
    }
    try {
        const content = JSON.stringify(config, null, 2);
        require('fs').writeFileSync(configPath, content, 'utf8');
    }
    catch (err) {
        console.error(`[Commands] Failed to save groups config to ${configPath}:`, err);
    }
}
/**
 * Get group for a command (from config or inferred from path)
 */
function getCommandGroup(commandName, relativePath, location, projectPath) {
    // First check custom assignments
    const config = loadGroupsConfig(location, projectPath);
    if (config.assignments[commandName]) {
        return config.assignments[commandName];
    }
    // Fallback to path-based inference - use full directory path as group
    const parts = relativePath.split(/[/\\]/);
    if (parts.length > 1) {
        // Use full directory path (excluding filename) as group
        // e.g., 'workflow/review/code-review.md' -> 'workflow/review'
        return parts.slice(0, -1).join('/');
    }
    return 'other';
}
/**
 * Recursively scan directory for command files
 */
function scanCommandsRecursive(baseDir, currentDir, location, projectPath) {
    const results = [];
    if (!existsSync(currentDir)) {
        return results;
    }
    try {
        const entries = readdirSync(currentDir, { withFileTypes: true });
        for (const entry of entries) {
            const fullPath = join(currentDir, entry.name);
            let relativePath = relative(baseDir, fullPath);
            if (entry.isDirectory()) {
                // Recursively scan subdirectories
                results.push(...scanCommandsRecursive(baseDir, fullPath, location, projectPath));
            }
            else if (entry.isFile()) {
                // Check for .md or .md.disabled files
                const isEnabled = entry.name.endsWith('.md') && !entry.name.endsWith('.md.disabled');
                const isDisabled = entry.name.endsWith('.md.disabled');
                if (isEnabled || isDisabled) {
                    try {
                        const content = readFileSync(fullPath, 'utf8');
                        const metadata = parseCommandFrontmatter(content);
                        // For disabled files, remove .disabled from relativePath for consistency
                        if (isDisabled) {
                            relativePath = relativePath.replace(/\.disabled$/, '');
                        }
                        const commandName = metadata.name || basename(relativePath, '.md');
                        // Get group from external config (not from frontmatter)
                        const group = getCommandGroup(commandName, relativePath, location, projectPath);
                        results.push({
                            name: commandName,
                            description: metadata.description,
                            group,
                            enabled: isEnabled,
                            location,
                            path: fullPath,
                            relativePath,
                            argumentHint: metadata.argumentHint,
                            allowedTools: metadata.allowedTools
                        });
                    }
                    catch (err) {
                        // Skip files that fail to read
                        console.error(`[Commands] Failed to read ${fullPath}:`, err);
                    }
                }
            }
        }
    }
    catch (err) {
        console.error(`[Commands] Failed to scan directory ${currentDir}:`, err);
    }
    return results;
}
/**
 * Get all commands configuration
 */
function getCommandsConfig(projectPath) {
    const result = {
        projectCommands: [],
        userCommands: [],
        groups: []
    };
    const groupSet = new Set();
    try {
        // Scan project commands (includes both .md and .md.disabled)
        const projectDir = getCommandsDir('project', projectPath);
        result.projectCommands = scanCommandsRecursive(projectDir, projectDir, 'project', projectPath);
        // Scan user commands (includes both .md and .md.disabled)
        const userDir = getCommandsDir('user', projectPath);
        result.userCommands = scanCommandsRecursive(userDir, userDir, 'user', projectPath);
        // Collect all groups
        for (const cmd of [...result.projectCommands, ...result.userCommands]) {
            groupSet.add(cmd.group);
        }
        result.groups = Array.from(groupSet).sort();
    }
    catch (error) {
        console.error('[Commands] Error reading commands config:', error);
    }
    return result;
}
/**
 * Find command by name in commands list
 */
function findCommand(commands, commandName) {
    // Try exact name match first
    let cmd = commands.find(c => c.name === commandName);
    if (cmd)
        return cmd;
    // Try matching by relative path (without extension)
    cmd = commands.find(c => {
        const pathWithoutExt = c.relativePath.replace(/\.md$/, '');
        return pathWithoutExt === commandName;
    });
    if (cmd)
        return cmd;
    // Try matching by filename (without extension)
    cmd = commands.find(c => {
        const filename = basename(c.relativePath, '.md');
        return filename === commandName;
    });
    return cmd;
}
/**
 * Toggle a command's enabled state
 */
async function toggleCommand(commandName, location, projectPath, initialPath) {
    try {
        // Validate command name
        if (commandName.includes('..')) {
            return { success: false, message: 'Invalid command name', status: 400 };
        }
        const config = getCommandsConfig(projectPath);
        const commands = location === 'project' ? config.projectCommands : config.userCommands;
        const command = findCommand(commands, commandName);
        if (!command) {
            return { success: false, message: 'Command not found', status: 404 };
        }
        const commandsDir = getCommandsDir(location, projectPath);
        // relativePath already includes .md extension (e.g., 'workflow/plan.md')
        const commandPath = join(commandsDir, command.relativePath);
        const disabledPath = commandPath + '.disabled';
        if (command.enabled) {
            // Disable: rename .md to .md.disabled
            if (!existsSync(commandPath)) {
                return { success: false, message: 'Command file not found', status: 404 };
            }
            if (existsSync(disabledPath)) {
                return { success: false, message: 'Command already disabled', status: 409 };
            }
            renameSync(commandPath, disabledPath);
            return {
                success: true,
                message: 'Command disabled',
                commandName: command.name,
                location
            };
        }
        else {
            // Enable: rename .md.disabled back to .md
            if (!existsSync(disabledPath)) {
                return { success: false, message: 'Disabled command not found', status: 404 };
            }
            if (existsSync(commandPath)) {
                return { success: false, message: 'Command already enabled', status: 409 };
            }
            renameSync(disabledPath, commandPath);
            return {
                success: true,
                message: 'Command enabled',
                commandName: command.name,
                location
            };
        }
    }
    catch (error) {
        return {
            success: false,
            message: error.message,
            status: 500
        };
    }
}
/**
 * Toggle all commands in a group
 */
async function toggleGroup(groupName, location, enable, projectPath, initialPath) {
    const config = getCommandsConfig(projectPath);
    const commands = location === 'project' ? config.projectCommands : config.userCommands;
    // Filter commands by group and current state
    const targetCommands = commands.filter(cmd => cmd.group === groupName && cmd.enabled !== enable);
    if (targetCommands.length === 0) {
        return {
            success: true,
            results: [],
            message: `No commands to ${enable ? 'enable' : 'disable'} in group '${groupName}'`
        };
    }
    const results = [];
    for (const cmd of targetCommands) {
        const result = await toggleCommand(cmd.name, location, projectPath, initialPath);
        results.push(result);
    }
    const successCount = results.filter(r => r.success).length;
    const failCount = results.filter(r => !r.success).length;
    return {
        success: failCount === 0,
        results,
        message: `${enable ? 'Enabled' : 'Disabled'} ${successCount} commands${failCount > 0 ? `, ${failCount} failed` : ''}`
    };
}
// ========== Route Handler ==========
/**
 * Handle Commands routes
 * @returns true if route was handled, false otherwise
 */
export async function handleCommandsRoutes(ctx) {
    const { pathname, url, req, res, initialPath, handlePostRequest } = ctx;
    // GET /api/commands - List all commands
    if (pathname === '/api/commands' && req.method === 'GET') {
        const projectPathParam = url.searchParams.get('path') || initialPath;
        try {
            const validatedProjectPath = await validateAllowedPath(projectPathParam, {
                mustExist: true,
                allowedDirectories: [initialPath]
            });
            const config = getCommandsConfig(validatedProjectPath);
            // Include groups config from both project and user
            const projectGroupsConfig = loadGroupsConfig('project', validatedProjectPath);
            const userGroupsConfig = loadGroupsConfig('user', validatedProjectPath);
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({
                ...config,
                projectGroupsConfig,
                userGroupsConfig
            }));
        }
        catch (err) {
            const message = err instanceof Error ? err.message : String(err);
            const status = message.includes('Access denied') ? 403 : 400;
            console.error(`[Commands] Project path validation failed: ${message}`);
            res.writeHead(status, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({
                error: status === 403 ? 'Access denied' : 'Invalid path',
                projectCommands: [],
                userCommands: [],
                groups: []
            }));
        }
        return true;
    }
    // POST /api/commands/:name/toggle - Toggle single command
    if (pathname.match(/^\/api\/commands\/[^/]+\/toggle$/) && req.method === 'POST') {
        const pathParts = pathname.split('/');
        const commandName = decodeURIComponent(pathParts[3]);
        handlePostRequest(req, res, async (body) => {
            if (!isRecord(body)) {
                return { error: 'Invalid request body', status: 400 };
            }
            const locationValue = body.location;
            const projectPathParam = typeof body.projectPath === 'string' ? body.projectPath : undefined;
            if (locationValue !== 'project' && locationValue !== 'user') {
                return { error: 'Location is required (project or user)' };
            }
            const projectPath = projectPathParam || initialPath;
            return toggleCommand(commandName, locationValue, projectPath, initialPath);
        });
        return true;
    }
    // POST /api/commands/group/:groupName/toggle - Toggle all commands in group
    if (pathname.match(/^\/api\/commands\/group\/[^/]+\/toggle$/) && req.method === 'POST') {
        const pathParts = pathname.split('/');
        const groupName = decodeURIComponent(pathParts[4]);
        handlePostRequest(req, res, async (body) => {
            if (!isRecord(body)) {
                return { error: 'Invalid request body', status: 400 };
            }
            const locationValue = body.location;
            const enable = body.enable === true;
            const projectPathParam = typeof body.projectPath === 'string' ? body.projectPath : undefined;
            if (locationValue !== 'project' && locationValue !== 'user') {
                return { error: 'Location is required (project or user)' };
            }
            const projectPath = projectPathParam || initialPath;
            return toggleGroup(groupName, locationValue, enable, projectPath, initialPath);
        });
        return true;
    }
    // GET /api/commands/groups - Get groups configuration
    if (pathname === '/api/commands/groups' && req.method === 'GET') {
        const projectPathParam = url.searchParams.get('path') || initialPath;
        const location = url.searchParams.get('location') || 'project';
        try {
            const validatedProjectPath = await validateAllowedPath(projectPathParam, {
                mustExist: true,
                allowedDirectories: [initialPath]
            });
            if (location !== 'project' && location !== 'user') {
                res.writeHead(400, { 'Content-Type': 'application/json' });
                res.end(JSON.stringify({ error: 'Invalid location' }));
                return true;
            }
            const groupsConfig = loadGroupsConfig(location, validatedProjectPath);
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify(groupsConfig));
        }
        catch (err) {
            const message = err instanceof Error ? err.message : String(err);
            const status = message.includes('Access denied') ? 403 : 400;
            res.writeHead(status, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({ error: message }));
        }
        return true;
    }
    // PUT /api/commands/groups - Update groups configuration
    if (pathname === '/api/commands/groups' && req.method === 'PUT') {
        const projectPathParam = url.searchParams.get('path') || initialPath;
        const location = url.searchParams.get('location') || 'project';
        handlePostRequest(req, res, async (body) => {
            try {
                const validatedProjectPath = await validateAllowedPath(projectPathParam, {
                    mustExist: true,
                    allowedDirectories: [initialPath]
                });
                if (location !== 'project' && location !== 'user') {
                    return { error: 'Invalid location', status: 400 };
                }
                if (!isRecord(body)) {
                    return { error: 'Invalid request body', status: 400 };
                }
                // Validate and save groups config
                const config = {
                    groups: isRecord(body.groups) ? body.groups : {},
                    assignments: isRecord(body.assignments) ? body.assignments : {}
                };
                saveGroupsConfig(location, validatedProjectPath, config);
                return {
                    success: true,
                    message: 'Groups configuration updated',
                    data: config,
                    status: 200
                };
            }
            catch (err) {
                const message = err instanceof Error ? err.message : String(err);
                const status = message.includes('Access denied') ? 403 : 400;
                console.error(`[Commands] Failed to update groups config: ${message}`);
                return { error: message, status };
            }
        });
        return true;
    }
    return false;
}
//# sourceMappingURL=commands-routes.js.map