import { existsSync } from 'node:fs';
import { join, relative } from 'node:path';
import chalk from 'chalk';
import select from '@inquirer/select';
import { getRepoRoot } from '../git.js';
import { loadWardenConfig, appendTrigger } from '../../config/index.js';
import { discoverAllSkills } from '../../skills/loader.js';
import { fetchRemote, discoverRemoteSkills, parseRemoteRef, } from '../../skills/remote.js';
import { ICON_CHECK } from '../output/icons.js';
/** Custom theme for select prompts - white for selected, gray for unselected */
const selectTheme = {
    prefix: {
        idle: '',
        done: '',
    },
    icon: {
        cursor: chalk.white('›'),
    },
    style: {
        message: () => '', // We print heading separately
        highlight: (text) => chalk.white(text),
        disabled: (text) => chalk.dim(text),
        description: (text) => chalk.white(text),
        keysHelpTip: (keys) => {
            const keyStr = keys.map(([key, action]) => `${key} ${action}`).join(', ');
            return `\n${chalk.dim(keyStr)}`;
        },
    },
};
/**
 * Render the list of available local skills.
 */
function renderSkillList(skills, configuredSkills, reporter) {
    reporter.bold('Available Skills');
    reporter.blank();
    // Sort skills alphabetically by name
    const sortedSkills = Array.from(skills.entries()).sort((a, b) => a[0].localeCompare(b[0]));
    for (const [name, discovered] of sortedSkills) {
        const isConfigured = configuredSkills.has(name);
        const dirTag = chalk.dim(`[${discovered.directory}]`);
        const configuredTag = isConfigured ? chalk.dim(' (already configured)') : '';
        if (reporter.mode.isTTY) {
            const icon = isConfigured ? chalk.dim(ICON_CHECK) : ' ';
            reporter.text(`  ${icon} ${chalk.bold(name)} ${dirTag}${configuredTag}`);
            reporter.text(`    ${chalk.dim(discovered.skill.description)}`);
        }
        else {
            const status = isConfigured ? '[configured]' : '';
            reporter.text(`${name} ${status} [${discovered.directory}]`);
            reporter.text(`  ${discovered.skill.description}`);
        }
    }
}
/**
 * Render the list of available remote skills.
 */
function renderRemoteSkillList(skills, configuredSkills, remote, reporter) {
    reporter.bold(`Available Skills from ${remote}`);
    reporter.blank();
    // Sort skills alphabetically by name
    const sortedSkills = [...skills].sort((a, b) => a.name.localeCompare(b.name));
    for (const skill of sortedSkills) {
        const isConfigured = configuredSkills.has(skill.name);
        const configuredTag = isConfigured ? chalk.dim(' (already configured)') : '';
        if (reporter.mode.isTTY) {
            const icon = isConfigured ? chalk.dim(ICON_CHECK) : ' ';
            reporter.text(`  ${icon} ${chalk.bold(skill.name)}${configuredTag}`);
            reporter.text(`    ${chalk.dim(skill.description)}`);
        }
        else {
            const status = isConfigured ? '[configured]' : '';
            reporter.text(`${skill.name} ${status}`);
            reporter.text(`  ${skill.description}`);
        }
    }
}
/**
 * Prompt user to select a local skill interactively.
 */
async function promptSkillSelection(skills, configuredSkills, reporter) {
    // Filter out already configured skills for selection
    const availableSkills = Array.from(skills.entries())
        .filter(([name]) => !configuredSkills.has(name))
        .sort((a, b) => a[0].localeCompare(b[0]));
    if (availableSkills.length === 0) {
        reporter.warning('All available skills are already configured.');
        return null;
    }
    const choices = availableSkills.map(([name, discovered]) => {
        return {
            name: `${name} ${chalk.dim(`[${discovered.directory}]`)}`,
            value: name,
            description: discovered.skill.description,
        };
    });
    reporter.bold('ADD SKILL');
    reporter.blank();
    try {
        const answer = await select({
            message: '',
            choices,
            theme: selectTheme,
        });
        // Clear the inquirer "done" line
        process.stderr.write('\x1b[1A\x1b[2K');
        return answer;
    }
    catch {
        // User cancelled (Ctrl+C or escape)
        return null;
    }
}
/**
 * Prompt user to select a remote skill interactively.
 */
async function promptRemoteSkillSelection(skills, configuredSkills, reporter) {
    // Filter out already configured skills for selection
    const availableSkills = skills
        .filter((s) => !configuredSkills.has(s.name))
        .sort((a, b) => a.name.localeCompare(b.name));
    if (availableSkills.length === 0) {
        reporter.warning('All available skills are already configured.');
        return null;
    }
    const choices = availableSkills.map((skill) => {
        return {
            name: skill.name,
            value: skill.name,
            description: skill.description,
        };
    });
    reporter.bold('ADD REMOTE SKILL');
    reporter.blank();
    try {
        const answer = await select({
            message: '',
            choices,
            theme: selectTheme,
        });
        // Clear the inquirer "done" line
        process.stderr.write('\x1b[1A\x1b[2K');
        return answer;
    }
    catch {
        // User cancelled (Ctrl+C or escape)
        return null;
    }
}
/**
 * Create a default trigger for a local skill.
 */
function createDefaultTrigger(skillName) {
    return {
        name: skillName,
        event: 'pull_request',
        actions: ['opened', 'synchronize', 'reopened'],
        skill: skillName,
    };
}
/**
 * Create a trigger for a remote skill.
 */
function createRemoteTrigger(skillName, remote) {
    return {
        name: skillName,
        event: 'pull_request',
        actions: ['opened', 'synchronize', 'reopened'],
        skill: skillName,
        remote,
    };
}
/**
 * Run the add command for remote skills.
 */
async function runAddRemote(options, reporter, configPath, configuredSkills) {
    const remote = options.remote ?? '';
    if (!remote) {
        reporter.error('Remote repository is required');
        return 1;
    }
    const cwd = process.cwd();
    // Validate remote ref format
    try {
        parseRemoteRef(remote);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        reporter.error(message);
        return 1;
    }
    // Fetch remote repository
    reporter.step(`Fetching skills from ${remote}...`);
    try {
        await fetchRemote(remote, {
            onProgress: (msg) => reporter.debug(msg),
        });
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        reporter.error(`Failed to fetch remote: ${message}`);
        return 1;
    }
    // Discover skills in the remote
    const remoteSkills = await discoverRemoteSkills(remote);
    if (remoteSkills.length === 0) {
        reporter.error(`No skills found in remote: ${remote}`);
        return 1;
    }
    reporter.success(`Found ${remoteSkills.length} skill${remoteSkills.length === 1 ? '' : 's'}`);
    // Handle --list: display remote skills and exit
    if (options.list) {
        reporter.blank();
        renderRemoteSkillList(remoteSkills, configuredSkills, remote, reporter);
        return 0;
    }
    // Get skill to add (from --skill or interactive prompt)
    let skillName;
    if (options.skill) {
        skillName = options.skill;
    }
    else if (reporter.mode.isTTY) {
        reporter.blank();
        skillName = await promptRemoteSkillSelection(remoteSkills, configuredSkills, reporter);
        if (!skillName) {
            return 0; // User quit or no skills available
        }
    }
    else {
        reporter.error('Skill name required when not running interactively.');
        reporter.tip(`Use: warden add --remote ${remote} --skill <name>`);
        return 1;
    }
    // Validate skill exists in remote
    const skill = remoteSkills.find((s) => s.name === skillName);
    if (!skill) {
        reporter.error(`Skill '${skillName}' not found in remote: ${remote}`);
        reporter.blank();
        reporter.tip('Available skills:');
        for (const s of remoteSkills) {
            reporter.text(`  - ${s.name}`);
        }
        return 1;
    }
    // Check for duplicate trigger
    if (configuredSkills.has(skillName)) {
        reporter.warning(`Trigger '${skillName}' already exists in warden.toml`);
        reporter.skipped(relative(cwd, configPath), 'trigger already configured');
        return 0;
    }
    // Append trigger to warden.toml
    const trigger = createRemoteTrigger(skillName, remote);
    try {
        appendTrigger(configPath, trigger);
        reporter.success(`Added trigger '${skillName}' to ${relative(cwd, configPath)}`);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        reporter.error(`Failed to update warden.toml: ${message}`);
        return 1;
    }
    // Show success message
    reporter.blank();
    reporter.text(`The trigger will run on pull requests using skill from ${chalk.cyan(remote)}.`);
    reporter.text(`Edit ${chalk.cyan('warden.toml')} to customize filters and output options.`);
    return 0;
}
/**
 * Run the add command.
 */
export async function runAdd(options, reporter) {
    const cwd = process.cwd();
    // 1. Check git repo
    let repoRoot;
    try {
        repoRoot = getRepoRoot(cwd);
    }
    catch {
        reporter.error('Not a git repository. Run this command from a git repository.');
        return 1;
    }
    // 2. Check warden.toml exists (deferred for --list without --remote)
    const configPath = join(repoRoot, 'warden.toml');
    const hasConfig = existsSync(configPath);
    // 3. Load existing config if available
    let configuredSkills = new Set();
    if (hasConfig) {
        try {
            const config = loadWardenConfig(repoRoot);
            configuredSkills = new Set(config.triggers.map((t) => t.name));
        }
        catch (err) {
            const message = err instanceof Error ? err.message : String(err);
            reporter.error(`Failed to load warden.toml: ${message}`);
            return 1;
        }
    }
    // 4. Handle remote skills with --remote flag
    if (options.remote) {
        // For remote skills, require warden.toml (unless --list)
        if (!hasConfig && !options.list) {
            reporter.error('warden.toml not found.');
            reporter.tip('Run `warden init` first to create the configuration file.');
            return 1;
        }
        return runAddRemote(options, reporter, configPath, configuredSkills);
    }
    // 5. Discover local skills
    const skills = await discoverAllSkills(repoRoot, {
        onWarning: (message) => reporter.warning(message),
    });
    if (skills.size === 0) {
        reporter.error('No skills found.');
        reporter.tip('Add skills to .warden/skills/, .agents/skills/, or .claude/skills/');
        reporter.tip('Or use --remote to add remote skills: warden add --remote owner/repo --skill name');
        return 1;
    }
    // 6. Handle --list: display skills and exit (works without warden.toml)
    if (options.list) {
        renderSkillList(skills, configuredSkills, reporter);
        return 0;
    }
    // 7. For adding skills, require warden.toml
    if (!hasConfig) {
        reporter.error('warden.toml not found.');
        reporter.tip('Run `warden init` first to create the configuration file.');
        return 1;
    }
    // 8. Get skill to add (from arg or interactive prompt)
    let skillName;
    if (options.skill) {
        // Non-interactive: skill specified as argument
        skillName = options.skill;
    }
    else if (reporter.mode.isTTY) {
        // Interactive mode
        skillName = await promptSkillSelection(skills, configuredSkills, reporter);
        if (!skillName) {
            return 0; // User quit or no skills available
        }
    }
    else {
        // Non-TTY and no skill specified
        reporter.error('Skill name required when not running interactively.');
        reporter.tip('Use: warden add <skill-name> or warden add --list');
        return 1;
    }
    // 9. Validate skill exists
    if (!skills.has(skillName)) {
        reporter.error(`Skill not found: ${skillName}`);
        reporter.blank();
        reporter.tip('Available skills:');
        for (const name of skills.keys()) {
            reporter.text(`  - ${name}`);
        }
        return 1;
    }
    // 10. Check for duplicate trigger
    if (configuredSkills.has(skillName)) {
        reporter.warning(`Trigger '${skillName}' already exists in warden.toml`);
        reporter.skipped(relative(cwd, configPath), 'trigger already configured');
        return 0;
    }
    // 11. Append trigger to warden.toml
    const trigger = createDefaultTrigger(skillName);
    try {
        appendTrigger(configPath, trigger);
        reporter.success(`Added trigger '${skillName}' to ${relative(cwd, configPath)}`);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        reporter.error(`Failed to update warden.toml: ${message}`);
        return 1;
    }
    // 12. Show success message with next steps
    reporter.blank();
    reporter.text(`The trigger will run on pull requests.`);
    reporter.text(`Edit ${chalk.cyan('warden.toml')} to customize filters and output options.`);
    return 0;
}
//# sourceMappingURL=add.js.map