/**
 * CLI Onboard Command
 *
 * Interactive setup wizard that walks new users through configuring Crewly.
 * Detects AI providers, installs missing tools, and installs agent skills
 * from the marketplace.
 *
 * Used directly via `crewly onboard`, by the curl install script, and
 * by the Electron desktop app.
 *
 * Supports non-interactive mode via `--yes` flag and direct template
 * selection via `--template <id>`.
 *
 * @module cli/commands/onboard
 */
import { createInterface } from 'readline';
import { execSync } from 'child_process';
import { mkdirSync, writeFileSync, existsSync, copyFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
import { randomUUID } from 'crypto';
import chalk from 'chalk';
import { checkSkillsInstalled, installAllSkills, countBundledSkills, } from '../utils/marketplace.js';
import { listTemplates, getTemplate, getTemplatesDir, } from '../utils/templates.js';
// ========================= Banner =========================
/**
 * Prints the Crewly ASCII art banner to stdout.
 */
export function printBanner() {
    console.log(chalk.cyan(`
   ____                    _
  / ___|_ __ _____      _| |_   _
 | |   | '__/ _ \\ \\ /\\ / / | | | |
 | |___| | |  __/\\ V  V /| | |_| |
  \\____|_|  \\___| \\_/\\_/ |_|\\__, |
                              |___/
`));
    console.log(chalk.bold('  Welcome to Crewly! Let\'s get you set up.\n'));
}
// ========================= Readline helpers =========================
/**
 * Creates a readline interface attached to stdin/stdout.
 *
 * @returns A readline interface
 */
export function createReadlineInterface() {
    return createInterface({
        input: process.stdin,
        output: process.stdout,
    });
}
/**
 * Prompts the user with a question and returns their answer.
 *
 * @param rl - Readline interface
 * @param question - The prompt text
 * @returns The user's response string
 */
function ask(rl, question) {
    return new Promise((resolve) => {
        rl.question(question, (answer) => {
            resolve(answer.trim());
        });
    });
}
// ========================= Step 1: Provider selection =========================
/**
 * Asks the user which AI coding assistant they use.
 *
 * Displays a numbered menu and returns the user's choice.
 *
 * @param rl - Readline interface
 * @returns The selected provider choice
 */
export async function selectProvider(rl) {
    console.log(chalk.bold('  Step 1/5: AI Provider'));
    console.log('  Which AI coding assistant do you use?\n');
    console.log('    1. Claude Code (Anthropic) ' + chalk.green('(recommended)'));
    console.log(chalk.gray('       Best code quality, strong reasoning'));
    console.log('    2. Gemini CLI (Google)');
    console.log(chalk.gray('       Free tier available, fast responses'));
    console.log('    3. Codex CLI (OpenAI)');
    console.log(chalk.gray('       GPT-powered coding assistant'));
    console.log('    4. All providers');
    console.log('    5. Skip\n');
    const choices = {
        '1': 'claude',
        '2': 'gemini',
        '3': 'codex',
        '4': 'both',
        '5': 'skip',
    };
    while (true) {
        const answer = await ask(rl, '  Enter choice (1-5): ');
        const choice = choices[answer];
        if (choice) {
            console.log('');
            return choice;
        }
        console.log(chalk.yellow('  Please enter 1, 2, 3, 4, or 5.'));
    }
}
// ========================= Step 2: Tool detection & install =========================
/**
 * Checks whether a CLI tool is installed by running `which <command>`.
 *
 * @param command - The command name to look for (e.g. "claude")
 * @returns True if the command is found on the PATH
 */
export function checkToolInstalled(command) {
    try {
        execSync(`which ${command}`, { stdio: 'pipe' });
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Gets the version of an installed CLI tool.
 *
 * @param command - The command name
 * @param versionFlag - The flag to get version (default: "--version")
 * @returns The version string or null if not determinable
 */
export function getToolVersion(command, versionFlag = '--version') {
    try {
        const output = execSync(`${command} ${versionFlag} 2>/dev/null`, {
            stdio: 'pipe',
            timeout: 10000,
        }).toString().trim();
        // Extract first version-like pattern
        const match = output.match(/\d+\.\d+[\w.-]*/);
        return match ? match[0] : output.split('\n')[0];
    }
    catch {
        return null;
    }
}
/**
 * Installs a tool via npm globally.
 *
 * @param displayName - Human-readable tool name for output
 * @param npmPackage - The npm package name to install
 * @returns True if installation succeeded
 */
export function installTool(displayName, npmPackage) {
    try {
        console.log(chalk.blue(`  Installing ${displayName}...`));
        execSync(`npm install -g ${npmPackage}`, {
            stdio: 'pipe',
            timeout: 120000,
        });
        console.log(chalk.green(`  ✓ ${displayName} installed`));
        return true;
    }
    catch (error) {
        const msg = error instanceof Error ? error.message : String(error);
        console.log(chalk.red(`  ✗ Failed to install ${displayName}: ${msg}`));
        console.log(chalk.gray(`  Try: sudo npm install -g ${npmPackage}`));
        return false;
    }
}
/** Map of provider choices to the tools they require */
const PROVIDER_TOOLS = {
    claude: [
        { displayName: 'Claude Code', command: 'claude', npmPackage: '@anthropic-ai/claude-code' },
    ],
    gemini: [
        { displayName: 'Gemini CLI', command: 'gemini', npmPackage: '@google/gemini-cli' },
    ],
    codex: [
        { displayName: 'Codex CLI', command: 'codex', npmPackage: '@openai/codex' },
    ],
    both: [
        { displayName: 'Claude Code', command: 'claude', npmPackage: '@anthropic-ai/claude-code' },
        { displayName: 'Gemini CLI', command: 'gemini', npmPackage: '@google/gemini-cli' },
        { displayName: 'Codex CLI', command: 'codex', npmPackage: '@openai/codex' },
    ],
    skip: [],
};
/**
 * Checks for and optionally installs the tools needed for the selected provider.
 *
 * For each required tool, checks if it's on the PATH. If missing, asks the user
 * whether to install it via npm. In non-interactive (--yes) mode, automatically
 * installs missing tools.
 *
 * @param rl - Readline interface
 * @param provider - The chosen provider
 * @param autoYes - When true, skip prompts and install missing tools automatically
 */
export async function ensureTools(rl, provider, autoYes = false) {
    console.log(chalk.bold('  Step 2/5: Tool Installation'));
    // Check for tmux (required for agent PTY sessions)
    if (checkToolInstalled('tmux')) {
        const tmuxVersion = getToolVersion('tmux', '-V');
        const versionStr = tmuxVersion ? ` (${tmuxVersion})` : '';
        console.log(chalk.green(`  ✓ tmux detected${versionStr}`));
    }
    else {
        console.log(chalk.red('  ✖ tmux not found!'));
        console.log(chalk.yellow('    tmux is required to manage agent terminal sessions.'));
        console.log(chalk.gray('    Install: brew install tmux (macOS) or sudo apt install tmux (Linux)'));
        console.log(chalk.gray('    Setup cannot continue without tmux.\n'));
        process.exit(1);
    }
    const tools = PROVIDER_TOOLS[provider] || [];
    if (tools.length === 0) {
        console.log(chalk.gray('  Skipped AI provider installation.\n'));
        return;
    }
    for (const tool of tools) {
        if (checkToolInstalled(tool.command)) {
            const version = getToolVersion(tool.command);
            const versionStr = version ? ` (v${version})` : '';
            console.log(chalk.green(`  ✓ ${tool.displayName} detected${versionStr}`));
        }
        else {
            console.log(chalk.yellow(`  ⚠ ${tool.displayName} not found.`));
            if (autoYes) {
                installTool(tool.displayName, tool.npmPackage);
            }
            else {
                const answer = await ask(rl, `  Install ${tool.displayName} now? [Y/n] `);
                if (answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
                    installTool(tool.displayName, tool.npmPackage);
                }
                else {
                    console.log(chalk.gray(`  Skipped ${tool.displayName} installation.`));
                }
            }
        }
    }
    console.log('');
}
// ========================= Step 3: Skills =========================
/**
 * Checks for and installs agent skills from the marketplace.
 *
 * If skills are already installed, reports the count. Otherwise, downloads
 * and installs all skills with progress feedback.
 */
export async function ensureSkills() {
    console.log(chalk.bold('  Step 3/5: Agent Skills'));
    console.log(chalk.gray('  Skills let agents communicate, manage memory, and coordinate tasks.\n'));
    try {
        const { installed, total } = await checkSkillsInstalled();
        if (installed >= total && total > 0) {
            console.log(chalk.green(`  ✓ ${installed} agent skills already installed\n`));
            return;
        }
        if (total === 0) {
            // Check for bundled skills as fallback
            const bundled = countBundledSkills();
            if (bundled > 0) {
                console.log(chalk.green(`  ✓ ${bundled} bundled skills available\n`));
            }
            else {
                console.log(chalk.yellow('  No skills available in the marketplace.\n'));
            }
            return;
        }
        console.log(chalk.blue(`  Installing ${total} agent skills from marketplace...`));
        const count = await installAllSkills((name, index, skillTotal) => {
            console.log(chalk.gray(`  [${index}/${skillTotal}] ${name}`));
        });
        console.log(chalk.green(`  ✓ ${count} skills installed\n`));
    }
    catch (error) {
        // Offline fallback: count bundled skills
        const bundled = countBundledSkills();
        if (bundled > 0) {
            console.log(chalk.green(`  ✓ ${bundled} bundled skills available (marketplace offline)`));
            console.log(chalk.gray('  Run \'crewly install --all\' later for additional marketplace skills.\n'));
        }
        else {
            const msg = error instanceof Error ? error.message : String(error);
            console.log(chalk.yellow(`  ⚠ Could not install skills: ${msg}`));
            console.log(chalk.gray('  Run \'crewly install --all\' later to install skills.\n'));
        }
    }
}
// ========================= Step 4: Team Template =========================
/**
 * Asks the user to pick a pre-built team template.
 *
 * Displays available templates with descriptions. The user can pick one
 * or skip to configure their team later through the dashboard.
 *
 * @param rl - Readline interface
 * @returns The selected template, or null if skipped
 */
export async function selectTemplate(rl) {
    console.log(chalk.bold('  Step 4/5: Team Template'));
    console.log('  Choose a pre-built team to get started quickly:\n');
    const templates = listTemplates();
    if (templates.length === 0) {
        console.log(chalk.gray('  No templates available.\n'));
        return null;
    }
    for (let i = 0; i < templates.length; i++) {
        const t = templates[i];
        const members = t.members.map(m => m.name).join(', ');
        console.log(`    ${i + 1}. ${chalk.bold(t.name)}`);
        console.log(chalk.gray(`       ${t.description}`));
        console.log(chalk.gray(`       Members: ${members}\n`));
    }
    console.log(`    ${templates.length + 1}. Skip (configure later in dashboard)\n`);
    const maxChoice = templates.length + 1;
    while (true) {
        const answer = await ask(rl, `  Enter choice (1-${maxChoice}): `);
        const num = parseInt(answer, 10);
        if (num >= 1 && num <= templates.length) {
            const selected = templates[num - 1];
            console.log(chalk.green(`  ✓ Selected: ${selected.name}\n`));
            return selected;
        }
        if (num === maxChoice || answer === '') {
            console.log(chalk.gray('  Skipped team template.\n'));
            return null;
        }
        console.log(chalk.yellow(`  Please enter 1-${maxChoice}.`));
    }
}
// ========================= Team Creation =========================
/**
 * Creates a team from a template by writing it to ~/.crewly/teams/{team-id}/config.json.
 *
 * Converts template members into full TeamMember objects with UUIDs, session names,
 * and default status fields. The team is immediately available when `crewly start` runs.
 *
 * @param template - The team template to create from
 * @param provider - The chosen AI provider to set as default runtime for members
 * @returns True if the team was created successfully
 */
export function createTeamFromTemplate(template, provider = 'claude') {
    const now = new Date().toISOString();
    const teamsDir = join(homedir(), '.crewly', 'teams', template.id);
    // Map ProviderChoice to RuntimeType
    const runtimeTypeMap = {
        'claude': 'claude-code',
        'gemini': 'gemini-cli',
        'codex': 'codex-cli',
        'both': 'claude-code', // Default to Claude if both are selected
        'skip': 'claude-code',
    };
    const runtimeType = runtimeTypeMap[provider] || 'claude-code';
    try {
        mkdirSync(teamsDir, { recursive: true });
        const members = template.members.map((m) => {
            const memberId = randomUUID().split('-')[0]; // short id
            const sessionName = `${template.id}-${m.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')}-${memberId}`;
            return {
                id: randomUUID(),
                name: m.name,
                role: m.role,
                sessionName,
                systemPrompt: m.systemPrompt,
                runtimeType,
                skillOverrides: m.skillOverrides || [],
                excludedRoleSkills: m.excludedRoleSkills || [],
                createdAt: now,
                updatedAt: now,
                agentStatus: 'inactive',
                workingStatus: 'idle',
            };
        });
        const teamConfig = {
            id: template.id,
            name: template.name,
            description: template.description,
            members,
            projectIds: [],
            createdAt: now,
            updatedAt: now,
        };
        writeFileSync(join(teamsDir, 'config.json'), JSON.stringify(teamConfig, null, 2) + '\n');
        return true;
    }
    catch (error) {
        const msg = error instanceof Error ? error.message : String(error);
        console.log(chalk.red(`  ✗ Failed to create team: ${msg}`));
        return false;
    }
}
// ========================= Directory Scaffolding =========================
/**
 * Scaffolds the .crewly/ directory in the current working directory.
 *
 * Creates the minimum directory structure needed for `crewly start` to work:
 * - .crewly/
 * - .crewly/docs/
 * - .crewly/memory/
 * - .crewly/tasks/
 * - .crewly/teams/
 *
 * If a template is provided, copies goals.md and team.json from the
 * template directory into .crewly/ (only if they exist in the template
 * and don't already exist in the target).
 *
 * If the directory already exists, reports it and moves on but still
 * copies any missing template files.
 *
 * @param projectDir - The project root directory (defaults to process.cwd())
 * @param template - Optional template to copy project files from
 * @returns True if the directory was created or already existed
 */
export function scaffoldCrewlyDirectory(projectDir = process.cwd(), template) {
    const crewlyDir = join(projectDir, '.crewly');
    let alreadyExisted = false;
    if (existsSync(crewlyDir)) {
        console.log(chalk.green('  ✓ .crewly/ directory already exists'));
        alreadyExisted = true;
    }
    try {
        if (!alreadyExisted) {
            const subdirs = ['docs', 'memory', 'tasks', 'teams'];
            for (const subdir of subdirs) {
                mkdirSync(join(crewlyDir, subdir), { recursive: true });
            }
            // Write minimal config.env
            writeFileSync(join(crewlyDir, 'config.env'), '# Crewly configuration\n# Add API keys and settings here\n');
            console.log(chalk.green('  ✓ .crewly/ directory created'));
        }
        // Copy template project files (goals.md, team.json) if available
        if (template) {
            copyTemplateProjectFiles(crewlyDir, template);
        }
        return true;
    }
    catch (error) {
        const msg = error instanceof Error ? error.message : String(error);
        console.log(chalk.red(`  ✗ Failed to create .crewly/ directory: ${msg}`));
        return false;
    }
}
/**
 * Copies project-level files (goals.md, team.json) from a template
 * directory into the .crewly/ scaffold directory.
 *
 * Only copies files that exist in the template and don't already
 * exist in the target directory (avoids overwriting user edits).
 *
 * @param crewlyDir - The .crewly/ directory to copy files into
 * @param template - The template whose directory to copy from
 */
export function copyTemplateProjectFiles(crewlyDir, template) {
    const templatesDir = getTemplatesDir();
    const templateDir = join(templatesDir, template.id);
    if (!existsSync(templateDir)) {
        return;
    }
    const filesToCopy = ['goals.md', 'team.json'];
    let copiedCount = 0;
    for (const fileName of filesToCopy) {
        const src = join(templateDir, fileName);
        const dest = join(crewlyDir, fileName);
        if (existsSync(src) && !existsSync(dest)) {
            copyFileSync(src, dest);
            copiedCount++;
        }
    }
    if (copiedCount > 0) {
        console.log(chalk.green(`  ✓ Copied ${copiedCount} template file(s) to .crewly/`));
    }
}
// ========================= Step 5: Summary =========================
/**
 * Prints the setup-complete summary with next-step instructions.
 *
 * If a team template was selected, includes instructions on how to
 * create the team from the dashboard.
 *
 * @param selectedTemplate - The template chosen during onboarding, or null
 * @param projectDir - The project directory path for next-steps output
 */
export function printSummary(selectedTemplate = null, projectDir) {
    console.log(chalk.bold('  Step 5/5: Done!'));
    console.log(chalk.green('  ✓ Setup complete!\n'));
    if (selectedTemplate) {
        console.log(`  Team: ${chalk.bold(selectedTemplate.name)}`);
        console.log(`  Members: ${selectedTemplate.members.map(m => m.name).join(', ')}\n`);
    }
    console.log(chalk.bold('  Next steps:\n'));
    if (projectDir && projectDir !== process.cwd()) {
        console.log(chalk.cyan(`    cd ${projectDir}`));
    }
    console.log(chalk.cyan('    crewly start\n'));
    if (selectedTemplate) {
        console.log('  Your team is ready. Open the dashboard to assign a project');
        console.log('  and start your agents.\n');
    }
}
// ========================= Main command =========================
/**
 * Runs the onboarding wizard.
 *
 * In interactive mode (default), walks the user through 5 steps:
 * 1. Choose an AI provider (Claude Code, Gemini CLI, both, or skip)
 * 2. Detect / install the chosen tool(s)
 * 3. Install agent skills from the marketplace
 * 4. Pick a team template (or skip)
 * 5. Print a success summary
 *
 * In non-interactive mode (--yes), uses defaults:
 * - Provider: claude
 * - Auto-install missing tools
 * - First available template (or --template flag)
 * - Scaffold .crewly/ directory
 *
 * @param options - Command options from Commander.js
 */
export async function onboardCommand(options = {}) {
    printBanner();
    const autoYes = options.yes === true;
    // Handle --template flag: look up template by ID
    let preselectedTemplate = null;
    if (options.template) {
        const found = getTemplate(options.template);
        if (found) {
            preselectedTemplate = found;
        }
        else {
            console.log(chalk.yellow(`  ⚠ Template "${options.template}" not found.`));
            const available = listTemplates();
            if (available.length > 0) {
                console.log(chalk.gray(`  Available templates: ${available.map(t => t.id).join(', ')}\n`));
            }
        }
    }
    if (autoYes) {
        // Non-interactive mode: use defaults
        const provider = 'claude';
        console.log(chalk.gray('  Running in non-interactive mode (--yes)\n'));
        // Step 1: Default provider
        console.log(chalk.bold('  Step 1/5: AI Provider'));
        console.log(chalk.green(`  ✓ Using default: Claude Code\n`));
        // Step 2: Auto-install tools
        await ensureTools(createReadlineInterface(), provider, true);
        // Step 3: Skills
        await ensureSkills();
        // Step 4: Template — use preselected or first available
        console.log(chalk.bold('  Step 4/5: Team Template'));
        const selectedTemplate = preselectedTemplate ?? listTemplates()[0] ?? null;
        if (selectedTemplate) {
            console.log(chalk.green(`  ✓ Using template: ${selectedTemplate.name}\n`));
            const created = createTeamFromTemplate(selectedTemplate, provider);
            if (created) {
                console.log(chalk.green(`  ✓ Team "${selectedTemplate.name}" created\n`));
            }
        }
        else {
            console.log(chalk.gray('  No templates available.\n'));
        }
        // Scaffold .crewly/ directory (with template project files)
        scaffoldCrewlyDirectory(process.cwd(), selectedTemplate);
        // Step 5: Summary
        printSummary(selectedTemplate);
        return;
    }
    // Interactive mode
    const rl = createReadlineInterface();
    try {
        // Step 1: Provider selection
        const provider = await selectProvider(rl);
        // Step 2: Tool installation
        await ensureTools(rl, provider);
        // Step 3: Skills
        await ensureSkills();
        // Step 4: Team template — preselected or interactive
        let selectedTemplate;
        if (preselectedTemplate) {
            console.log(chalk.bold('  Step 4/5: Team Template'));
            console.log(chalk.green(`  ✓ Using template: ${preselectedTemplate.name}\n`));
            selectedTemplate = preselectedTemplate;
        }
        else {
            selectedTemplate = await selectTemplate(rl);
        }
        // Create team from selected template
        if (selectedTemplate) {
            const created = createTeamFromTemplate(selectedTemplate, provider);
            if (created) {
                console.log(chalk.green(`  ✓ Team "${selectedTemplate.name}" created\n`));
            }
        }
        // Scaffold .crewly/ directory (with template project files)
        scaffoldCrewlyDirectory(process.cwd(), selectedTemplate);
        // Step 5: Summary
        printSummary(selectedTemplate);
    }
    finally {
        rl.close();
    }
}
//# sourceMappingURL=onboard.js.map