import chalk from 'chalk';
import { createWorkspaceSkillSkippedReport, generateWorkspaceAgentSkills, getWorkspaceSkillCapableTools, getWorkspaceSkillToolIds, getWorkspaceOpenerLabel, parseWorkspaceSkillToolsValue, updateWorkspaceAgentSkills, listKnownWorkspaceEntries, readWorkspaceViewState, syncWorkspaceOpenSurface, writeWorkspaceViewState, } from '../core/workspace/index.js';
import { isInteractive, resolveNoInteractive } from '../utils/interactive.js';
import { addWorkspaceLink, createManagedWorkspace, loadWorkspaceForDoctor, loadWorkspaceForList, parseSetupLinks, readWorkspaceForMutation, updateWorkspaceLink, validateWorkspaceNameForSetup, } from './workspace/operations.js';
import { selectWorkspaceForCommand } from './workspace/selection.js';
import { launchWorkspaceOpenCommand, } from './workspace/open.js';
import { buildWorkspaceOpenJsonPayload, prepareWorkspaceOpen, } from './workspace/open-view.js';
import { getPreferredWorkspaceSkillAgentId, parseSetupOpenerOption, promptPreferredOpener, } from './workspace/opener-selection.js';
import { workspacePromptTheme } from './workspace/prompt-theme.js';
import { registerWorkspaceCommandWith } from './workspace/registration.js';
import { promptSetupLinks } from './workspace/setup-prompts.js';
import { WorkspaceCliError, appendStatus, asErrorMessage, asStatus, } from './workspace/types.js';
function printJson(payload) {
    console.log(JSON.stringify(payload, null, 2));
}
function printWorkspaceSetupIntro() {
    console.log(chalk.bold('Workspace setup'));
    console.log('');
}
function isPromptCancellationError(error) {
    return (error instanceof Error &&
        (error.name === 'ExitPromptError' || error.message.includes('force closed the prompt with SIGINT')));
}
async function promptWorkspaceName(initialName) {
    if (initialName) {
        return validateWorkspaceNameForSetup(initialName);
    }
    const { input } = await import('@inquirer/prompts');
    console.log(chalk.bold('[1/5] Name the workspace'));
    console.log(chalk.dim('Use a stable name for the repo group, e.g. platform.'));
    console.log('');
    return input({
        message: 'Workspace name:',
        required: true,
        theme: workspacePromptTheme,
        validate(value) {
            try {
                validateWorkspaceNameForSetup(value);
                return true;
            }
            catch {
                return 'Workspace names must be kebab-case with lowercase letters, numbers, and single hyphen separators.';
            }
        },
    });
}
function parseSetupToolsOption(tools) {
    try {
        return parseWorkspaceSkillToolsValue(tools);
    }
    catch (error) {
        throw new WorkspaceCliError(asErrorMessage(error), 'invalid_workspace_setup_tools', {
            target: 'workspace.skills',
            fix: `Use --tools all, --tools none, or one of: ${getWorkspaceSkillToolIds().join(', ')}`,
        });
    }
}
function parseUpdateToolsOption(tools) {
    try {
        return parseWorkspaceSkillToolsValue(tools);
    }
    catch (error) {
        throw new WorkspaceCliError(asErrorMessage(error), 'invalid_workspace_update_tools', {
            target: 'workspace.skills',
            fix: `Use --tools all, --tools none, or one of: ${getWorkspaceSkillToolIds().join(', ')}`,
        });
    }
}
async function promptWorkspaceSkillAgents(preferredOpener) {
    const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
    const preferredAgentId = getPreferredWorkspaceSkillAgentId(preferredOpener);
    const tools = getWorkspaceSkillCapableTools();
    const sortedChoices = tools
        .map((tool) => ({
        name: tool.name,
        value: tool.value,
        preSelected: tool.value === preferredAgentId,
    }))
        .sort((a, b) => {
        if (a.preSelected !== b.preSelected) {
            return a.preSelected ? -1 : 1;
        }
        return a.name.localeCompare(b.name);
    });
    if (preferredAgentId) {
        const preferredTool = tools.find((tool) => tool.value === preferredAgentId);
        if (preferredTool) {
            console.log(`${preferredTool.name} matches your preferred opener and is pre-selected.`);
        }
    }
    return searchableMultiSelect({
        message: 'Which agents should get OpenSpec skills in this workspace?',
        pageSize: 15,
        choices: sortedChoices,
    });
}
function printStatusLines(statuses) {
    for (const status of statuses) {
        const label = status.severity === 'warning' ? 'Warning' : 'Issue';
        console.log(`${label}: ${status.message}`);
        if (status.fix) {
            console.log(`Fix: ${status.fix}`);
        }
    }
}
function printLinksHuman(links) {
    if (links.length === 0) {
        console.log('  (no linked repos or folders)');
        return;
    }
    for (const link of links) {
        const suffix = link.status.some((status) => status.severity === 'error') ? ' [issue]' : '';
        console.log(`  ${link.name} -> ${link.path ?? '(no local path recorded)'}${suffix}`);
        if (link.repo_specs_path) {
            console.log(`    repo specs: ${link.repo_specs_path}`);
        }
    }
}
function collectWorkspaceIssues(workspace) {
    return [
        ...workspace.status,
        ...workspace.links.flatMap((link) => link.status),
    ];
}
function printDoctorHuman(result) {
    console.log(`Workspace: ${result.workspace.name}`);
    console.log(`Location: ${result.workspace.root}`);
    if (result.workspace.context) {
        const selector = result.workspace.context.store_selector;
        const suffix = selector.kind === 'path' ? ` via ${selector.path}` : '';
        console.log(`Context: ${result.workspace.context.store}/${result.workspace.context.initiative}${suffix}`);
    }
    else {
        console.log('Context: (none)');
    }
    console.log('');
    printStatusLines(result.status);
    if (result.status.length > 0) {
        console.log('');
    }
    console.log('Linked repos or folders:');
    printLinksHuman(result.workspace.links);
    const issues = collectWorkspaceIssues(result.workspace);
    console.log('');
    console.log('Advisory edit boundaries:');
    if (result.workspace.context) {
        console.log('  Initiative/context-store files are shared coordination context.');
    }
    else {
        console.log('  No initiative coordination context is attached.');
    }
    console.log('  Linked repos and folders are local implementation context when selected.');
    if (issues.length === 0) {
        console.log('');
        console.log('No workspace issues found.');
        return;
    }
    console.log('');
    console.log('Issues:');
    for (const issue of issues) {
        console.log(`  - ${issue.message}`);
        if (issue.target) {
            console.log(`    Target: ${issue.target}`);
        }
        if (issue.fix) {
            console.log(`    Fix: ${issue.fix}`);
        }
    }
}
function printWorkspaceListHuman(workspaces) {
    console.log(chalk.bold(`OpenSpec workspaces (${workspaces.length})`));
    for (const workspace of workspaces) {
        console.log('');
        console.log(chalk.bold(workspace.name));
        console.log(`  Location: ${workspace.root}`);
        if (workspace.status.length > 0) {
            console.log('  Status:');
            for (const status of workspace.status) {
                const statusLabel = status.severity === 'warning' ? chalk.yellow('Warning') : chalk.red('Issue');
                console.log(`    ${statusLabel}: ${status.message}`);
                if (status.fix) {
                    console.log(`    Fix: ${status.fix}`);
                }
            }
        }
        console.log(`  Linked repos or folders (${workspace.links.length}):`);
        if (workspace.links.length === 0) {
            console.log(chalk.dim('    (none)'));
            continue;
        }
        for (const link of workspace.links) {
            const suffix = link.status.some((status) => status.severity === 'error') ? chalk.red(' [issue]') : '';
            console.log(`    ${link.name} -> ${link.path ?? '(no local path recorded)'}${suffix}`);
            if (link.repo_specs_path) {
                console.log(chalk.dim(`      repo specs: ${link.repo_specs_path}`));
            }
        }
    }
}
function printWorkspaceCheckSummaryHuman(result) {
    printStatusLines(result.status);
    const issues = collectWorkspaceIssues(result.workspace);
    if (issues.length === 0) {
        console.log('  No workspace issues found.');
        return;
    }
    console.log('  Issues:');
    for (const issue of issues) {
        console.log(`    - ${issue.message}`);
        if (issue.target) {
            console.log(`      Target: ${issue.target}`);
        }
        if (issue.fix) {
            console.log(`      Fix: ${issue.fix}`);
        }
    }
}
function printLinkMutationHuman(heading, payload) {
    printStatusLines(payload.status);
    console.log(heading);
    console.log(`  ${payload.link.name} -> ${payload.link.path}`);
    console.log(`Workspace: ${payload.workspace.name}`);
}
function formatWorkspaceSkillAgentResult(result) {
    const workflowCount = result.workflow_ids?.length ?? 0;
    const workflowLabel = workflowCount === 1 ? '1 workflow' : `${workflowCount} workflows`;
    return `${result.name} (${workflowLabel})`;
}
function formatWorkspaceSkillRemovedResult(result) {
    const workflowCount = result.workflow_ids?.length ?? 0;
    const workflowLabel = workflowCount === 1 ? '1 workflow' : `${workflowCount} workflows`;
    return `${result.name} (${workflowLabel} removed)`;
}
function printWorkspaceSkillReportHuman(report) {
    console.log('Agent skills:');
    console.log(`  Profile: ${report.profile}`);
    console.log(`  Workflows: ${report.workflow_ids.length > 0 ? report.workflow_ids.join(', ') : '(none selected)'}`);
    if (report.generated.length > 0) {
        console.log(`  Generated: ${report.generated.map(formatWorkspaceSkillAgentResult).join(', ')}`);
    }
    if (report.added.length > 0) {
        console.log(`  Added: ${report.added.map(formatWorkspaceSkillAgentResult).join(', ')}`);
    }
    if (report.refreshed.length > 0) {
        console.log(`  Refreshed: ${report.refreshed.map(formatWorkspaceSkillAgentResult).join(', ')}`);
    }
    if (report.removed.length > 0) {
        console.log(`  Removed: ${report.removed.map(formatWorkspaceSkillRemovedResult).join(', ')}`);
    }
    if (report.skipped.length > 0) {
        for (const skipped of report.skipped) {
            const prefix = skipped.name ? `${skipped.name}: ` : '';
            console.log(`  Skipped: ${prefix}${skipped.message}`);
        }
    }
    if (report.failed.length > 0) {
        console.log(chalk.red(`  Failed: ${report.failed.map((failure) => `${failure.name} (${failure.error})`).join(', ')}`));
    }
    if (report.delivery_notice) {
        console.log(chalk.dim(`  ${report.delivery_notice}`));
    }
}
function hasWorkspaceSkillFailures(report) {
    return report.failed.length > 0;
}
function setWorkspaceSkillFailureExitCode(report) {
    if (hasWorkspaceSkillFailures(report)) {
        process.exitCode = 1;
    }
}
async function writeWorkspaceSkillState(workspaceRoot, selectedAgentIds, report) {
    const viewState = await readWorkspaceViewState(workspaceRoot);
    await writeWorkspaceViewState(workspaceRoot, {
        ...viewState,
        workspace_skills: {
            selected_agents: selectedAgentIds,
            last_applied_profile: report.profile,
            last_applied_delivery: report.delivery,
            last_applied_workflow_ids: report.workflow_ids,
            last_applied_at: new Date().toISOString(),
        },
    });
}
function resolveUpdateWorkspaceName(positionalName, options) {
    if (positionalName && options.workspace && positionalName !== options.workspace) {
        throw new WorkspaceCliError(`Conflicting workspace selectors: positional '${positionalName}' and --workspace '${options.workspace}'.`, 'workspace_selection_conflict', {
            target: 'workspace.name',
            fix: 'Use either the positional workspace name or --workspace with the same value.',
        });
    }
    return positionalName ?? options.workspace;
}
function printWorkspaceOpenHuman(prepared) {
    console.log(`Opening workspace: ${prepared.selected.name}`);
    console.log(`Location: ${prepared.selected.root}`);
    if (prepared.initiative) {
        console.log(`Initiative: ${prepared.initiative.store}/${prepared.initiative.id}`);
        console.log(`Initiative path: ${prepared.initiative.root}`);
    }
    console.log(`Opener: ${getWorkspaceOpenerLabel(prepared.opener)}`);
    if (prepared.skipped.length === 0) {
        return;
    }
    console.log('');
    console.log('Skipped linked repos or folders:');
    for (const link of prepared.skipped) {
        const location = link.path ?? '(no local path recorded)';
        console.log(`  ${link.name} -> ${location}`);
    }
    console.log('Repair skipped links with openspec workspace doctor.');
}
class WorkspaceCommand {
    async setup(options = {}) {
        try {
            const noInteractive = resolveNoInteractive(options);
            if (options.json && !noInteractive) {
                throw new WorkspaceCliError('workspace setup --json requires --no-interactive.', 'setup_json_requires_no_interactive', {
                    fix: 'openspec workspace setup --no-interactive --json --name <name> --link <path>',
                });
            }
            const interactive = !noInteractive && isInteractive(options);
            if (interactive) {
                printWorkspaceSetupIntro();
            }
            if (!interactive && (!options.name || (options.link ?? []).length === 0)) {
                throw new WorkspaceCliError('workspace setup --no-interactive requires --name <name> and at least one --link <path>.', 'missing_setup_inputs', {
                    fix: 'openspec workspace setup --no-interactive --name platform --link /path/to/repo',
                });
            }
            const workspaceName = interactive
                ? await promptWorkspaceName(options.name)
                : validateWorkspaceNameForSetup(options.name ?? '');
            const links = interactive ? await promptSetupLinks() : await parseSetupLinks(options.link);
            if (interactive) {
                console.log('');
                console.log(chalk.bold('[3/5] Choose preferred opener'));
            }
            const preferredOpener = interactive
                ? await promptPreferredOpener('Preferred opener:')
                : parseSetupOpenerOption(options.opener);
            let selectedWorkspaceSkillAgents;
            if (options.tools !== undefined) {
                selectedWorkspaceSkillAgents = parseSetupToolsOption(options.tools);
            }
            else if (interactive) {
                console.log('');
                console.log(chalk.bold('[4/5] Install agent skills'));
                console.log(chalk.dim('Choose which coding agents should get OpenSpec skills in this workspace.'));
                console.log(chalk.dim('Press Enter with no agents selected to skip skill installation for now.'));
                console.log('');
                selectedWorkspaceSkillAgents = await promptWorkspaceSkillAgents(preferredOpener);
            }
            if (Object.keys(links).length === 0) {
                throw new WorkspaceCliError('workspace setup --no-interactive requires --name <name> and at least one --link <path>.', 'missing_setup_inputs', {
                    fix: 'openspec workspace setup --no-interactive --name platform --link /path/to/repo',
                });
            }
            if (interactive) {
                console.log('');
                console.log(chalk.bold('[5/5] Create workspace files'));
            }
            const workspace = await createManagedWorkspace(workspaceName, links, preferredOpener);
            const skillReport = selectedWorkspaceSkillAgents === undefined
                ? createWorkspaceSkillSkippedReport('tools_omitted', 'No workspace skills were installed. Run openspec workspace update --tools <ids> to install them later.')
                : await generateWorkspaceAgentSkills(workspace.root, selectedWorkspaceSkillAgents);
            if (selectedWorkspaceSkillAgents !== undefined && !hasWorkspaceSkillFailures(skillReport)) {
                await writeWorkspaceSkillState(workspace.root, selectedWorkspaceSkillAgents, skillReport);
            }
            const doctorResult = await loadWorkspaceForDoctor({
                name: workspace.name,
                root: workspace.root,
                status: [],
                unregisteredCurrentWorkspace: false,
            });
            if (options.json) {
                printJson({
                    workspace: doctorResult.workspace,
                    workspace_skills: skillReport,
                    status: doctorResult.status,
                });
                setWorkspaceSkillFailureExitCode(skillReport);
                return;
            }
            console.log(chalk.green('Workspace setup complete'));
            console.log('');
            printWorkspaceListHuman([doctorResult.workspace]);
            console.log('');
            console.log('Workspace check:');
            printWorkspaceCheckSummaryHuman(doctorResult);
            console.log('');
            printWorkspaceSkillReportHuman(skillReport);
            console.log('');
            console.log('Next useful commands:');
            console.log(`  openspec workspace doctor --workspace ${workspace.name}`);
            console.log(`  openspec workspace update --workspace ${workspace.name} --tools <ids>`);
            console.log('  openspec workspace list');
            setWorkspaceSkillFailureExitCode(skillReport);
        }
        catch (error) {
            this.handleFailure(options.json, { workspace: null, status: [] }, error);
        }
    }
    async list(options = {}) {
        try {
            const entries = await listKnownWorkspaceEntries();
            const workspaces = await Promise.all(entries.map((entry) => loadWorkspaceForList(entry)));
            const payload = { workspaces, status: [] };
            if (options.json) {
                printJson(payload);
                return;
            }
            if (workspaces.length === 0) {
                console.log("No OpenSpec workspaces found. Run 'openspec workspace setup' first.");
                return;
            }
            printWorkspaceListHuman(workspaces);
        }
        catch (error) {
            this.handleFailure(options.json, { workspaces: [], status: [] }, error);
        }
    }
    async link(nameOrPath, linkPath, options = {}) {
        try {
            if (!nameOrPath) {
                throw new WorkspaceCliError('workspace link requires a repo or folder path.', 'missing_link_path', {
                    fix: 'openspec workspace link /path/to/repo',
                });
            }
            const selected = await selectWorkspaceForCommand(options, 'link');
            const payload = await addWorkspaceLink(selected, nameOrPath, linkPath);
            if (options.json) {
                printJson(payload);
                return;
            }
            printLinkMutationHuman('Linked repo or folder:', payload);
        }
        catch (error) {
            this.handleFailure(options.json, { workspace: null, link: null, status: [] }, error);
        }
    }
    async relink(linkNameInput, linkPath, options = {}) {
        try {
            if (!linkNameInput || !linkPath) {
                throw new WorkspaceCliError('workspace relink requires a link name and repo or folder path.', 'missing_relink_arguments', {
                    fix: 'openspec workspace relink <name> /path/to/repo',
                });
            }
            const selected = await selectWorkspaceForCommand(options, 'relink');
            const payload = await updateWorkspaceLink(selected, linkNameInput, linkPath);
            if (options.json) {
                printJson(payload);
                return;
            }
            printLinkMutationHuman('Relinked repo or folder:', payload);
        }
        catch (error) {
            this.handleFailure(options.json, { workspace: null, link: null, status: [] }, error);
        }
    }
    async doctor(options = {}) {
        try {
            const selected = await selectWorkspaceForCommand(options, 'doctor');
            const result = await loadWorkspaceForDoctor(selected);
            if (options.json) {
                printJson(result);
                return;
            }
            printDoctorHuman(result);
        }
        catch (error) {
            this.handleFailure(options.json, { workspace: null, status: [] }, error);
        }
    }
    async update(positionalName, options = {}) {
        try {
            const workspaceName = resolveUpdateWorkspaceName(positionalName, options);
            const selected = await selectWorkspaceForCommand({
                ...options,
                workspace: workspaceName,
            }, 'update', { preferPositionalName: Boolean(positionalName) });
            await this.updateSelected(selected, options);
        }
        catch (error) {
            this.handleFailure(options.json, { workspace: null, workspace_skills: null, status: [] }, error);
        }
    }
    async updateSelected(selected, options) {
        const viewState = await readWorkspaceForMutation(selected);
        await syncWorkspaceOpenSurface(selected.root, viewState);
        const hasExplicitToolSelection = options.tools !== undefined;
        const selectedAgentIds = hasExplicitToolSelection
            ? parseUpdateToolsOption(options.tools ?? '')
            : viewState.workspace_skills?.selected_agents ?? [];
        const previousSkillState = hasExplicitToolSelection
            ? viewState.workspace_skills ?? { selected_agents: [] }
            : viewState.workspace_skills;
        const skillReport = await updateWorkspaceAgentSkills(selected.root, selectedAgentIds, previousSkillState);
        const shouldStoreSelection = hasExplicitToolSelection || Boolean(viewState.workspace_skills);
        if (shouldStoreSelection && !hasWorkspaceSkillFailures(skillReport)) {
            await writeWorkspaceSkillState(selected.root, selectedAgentIds, skillReport);
        }
        const doctorResult = await loadWorkspaceForDoctor(selected);
        if (options.json) {
            printJson({
                workspace: doctorResult.workspace,
                workspace_skills: skillReport,
                status: doctorResult.status,
            });
            setWorkspaceSkillFailureExitCode(skillReport);
            return;
        }
        console.log(chalk.green('Workspace update complete'));
        console.log(`Workspace: ${doctorResult.workspace.name}`);
        console.log(`Location: ${doctorResult.workspace.root}`);
        console.log('');
        printStatusLines(doctorResult.status);
        if (doctorResult.status.length > 0) {
            console.log('');
        }
        printWorkspaceSkillReportHuman(skillReport);
        console.log('');
        console.log('Next useful commands:');
        console.log(`  openspec workspace doctor --workspace ${doctorResult.workspace.name}`);
        console.log(`  openspec workspace update --workspace ${doctorResult.workspace.name} --tools <ids>`);
        setWorkspaceSkillFailureExitCode(skillReport);
    }
    async open(positionalName, options = {}) {
        try {
            const prepared = await prepareWorkspaceOpen(positionalName, options);
            if (!options.json) {
                printStatusLines(prepared.selected.status);
                if (prepared.selected.status.length > 0) {
                    console.log('');
                }
                printWorkspaceOpenHuman(prepared);
            }
            await launchWorkspaceOpenCommand(prepared.command, {
                stdio: options.json ? 'ignore' : 'inherit',
            });
            if (options.json) {
                printJson(buildWorkspaceOpenJsonPayload(prepared));
            }
        }
        catch (error) {
            this.handleFailure(options.json, { workspace: null, status: [] }, error);
        }
    }
    handleFailure(json, payload, error) {
        if (!json && isPromptCancellationError(error)) {
            console.error('Cancelled.');
            process.exitCode = 130;
            return;
        }
        if (json) {
            printJson(appendStatus(payload, asStatus(error)));
            process.exitCode = 1;
            return;
        }
        const status = asStatus(error);
        console.error(`Error: ${status.message}`);
        if (status.fix) {
            console.error(`Fix: ${status.fix}`);
        }
        process.exitCode = 1;
    }
}
export async function runWorkspaceUpdate(positionalName, options = {}) {
    const workspaceCommand = new WorkspaceCommand();
    await workspaceCommand.update(positionalName, options);
}
export function registerWorkspaceCommand(program) {
    registerWorkspaceCommandWith(program, new WorkspaceCommand());
}
//# sourceMappingURL=workspace.js.map