import { inferExternalSkillProviderFromPath } from '../../app/resources/external-skill-roots.js';
import { getBundledSkillsDir } from '../../config.js';
import { Spacer, Text } from '../../tui/index.js';
import { DynamicBorder } from '../components/primitives/dynamic-border.js';
import { SkillsSelectorComponent } from '../components/selectors/index.js';
import { theme } from '../theme/runtime.js';
export class SkillsWorkflow {
    deps;
    constructor(deps) {
        this.deps = deps;
    }
    showSkillsSelector() {
        const items = this.deps.session.resourceLoader
            .getSkills()
            .skills.map((skill) => ({
            name: skill.name,
            description: skill.description,
            enabled: !this.deps.settingsManager.isSkillDisabled(skill.name),
            locked: skill.disableModelInvocation,
            filePath: skill.filePath,
            source: this.getSkillDisplaySource(skill.filePath, skill.source)
        }));
        this.deps.showSelector((done) => {
            const selector = new SkillsSelectorComponent(items, () => {
                done();
                this.deps.requestRender();
            }, () => void this.deps.shutdown(), (draftItems) => {
                let changed = false;
                for (const item of draftItems) {
                    if (item.locked) {
                        continue;
                    }
                    const currentlyEnabled = !this.deps.settingsManager.isSkillDisabled(item.name);
                    if (currentlyEnabled === item.enabled) {
                        continue;
                    }
                    changed = true;
                    if (item.enabled) {
                        this.deps.settingsManager.enableSkill(item.name);
                    }
                    else {
                        this.deps.settingsManager.disableSkill(item.name);
                    }
                }
                done();
                if (!changed) {
                    this.deps.requestRender();
                    return;
                }
                this.deps.session.replaceToolsByName(this.deps.session.getToolNames());
                this.deps.showStatus('Skill visibility updated. Takes effect next turn.');
                this.deps.showWarning('Changing skill visibility invalidates prompt caching for this conversation, so upcoming turns may cost more until the new prompt is cached.');
                this.deps.requestRender();
            }, () => this.deps.requestRender());
            return { component: selector, focus: selector.getSkillList() };
        });
    }
    showDisabledSkillsWarning() {
        const disabled = this.getLoadedDisabledSkills();
        if (disabled.length === 0) {
            return false;
        }
        this.deps.showInfo('[Skill] Some skills are hidden from the system prompt. The agent will not know they exist unless you invoke them with /skill:name. Use /skills to review.');
        return true;
    }
    getLoadedDisabledSkills() {
        const loadedSkillNames = new Set(this.deps.session.resourceLoader.getSkills().skills.map((skill) => skill.name));
        return this.deps.settingsManager
            .getDisabledSkills()
            .filter((name) => loadedSkillNames.has(name));
    }
    async handleDownloadSkillsCommand() {
        if (!this.deps.downloadSkillsWithApproval) {
            this.deps.showWarning('Skill download is not available (not logged in?)');
            return;
        }
        this.deps.showStatus('Downloading skills from cloud...');
        this.deps.requestRender();
        try {
            const result = await this.deps.downloadSkillsWithApproval((request) => {
                const totalFiles = request.modifiedFiles + request.localOnlyFiles + request.remoteOnlyFiles;
                const details = request.kind === 'create'
                    ? [
                        'Create this skill locally from the cloud copy.',
                        `This skill contains ${request.remoteOnlyFiles} ${this.pluralizeFile(request.remoteOnlyFiles)}.`
                    ]
                    : [
                        'Replace the local skill folder with the cloud copy.',
                        `This will change ${totalFiles} ${this.pluralizeFile(totalFiles)} in the local skill folder.`
                    ];
                details.push(`Cloud source: ${request.source}`);
                details.push(...this.formatTimestampDetails(request.localModifiedAt, request.cloudModifiedAt));
                details.push(...this.formatFileDiffDetails(request.fileDiffs, 'download'));
                return this.deps.showConfirm(`Skill "${request.skillName}"`, details.join('\n'));
            });
            await this.deps.reloadSkills();
            const lines = [];
            if (result.downloaded.length > 0) {
                lines.push(`  ${theme.fg('success', 'Downloaded:')}`);
                for (const name of result.downloaded) {
                    lines.push(`    ${theme.fg('accent', name)}`);
                    const change = result.changes?.find((item) => item.skillName === name);
                    if (change) {
                        lines.push(...this.formatRenderedChange(change, 'download'));
                    }
                }
            }
            if (result.skipped.length > 0) {
                lines.push(`  ${theme.fg('dim', 'Unchanged:')}`);
                for (const name of result.skipped) {
                    lines.push(`    ${theme.fg('dim', `UM ${name}`)}`);
                }
            }
            if (result.skippedByUser.length > 0) {
                lines.push(`  ${theme.fg('dim', `Skipped: ${result.skippedByUser.join(', ')}`)}`);
            }
            if (result.errors.length > 0) {
                for (const error of result.errors) {
                    lines.push(`  ${theme.fg('error', error)}`);
                }
            }
            if (lines.length === 0) {
                lines.push('  No skills found.');
            }
            lines.push(`  ${theme.fg('dim', this.fileDiffLegend())}`);
            this.renderSummary('Skills Download', lines);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            this.deps.showWarning(`Skill download failed: ${message}`);
        }
    }
    async handleUploadSkillsCommand() {
        if (!this.deps.uploadSkills) {
            this.deps.showWarning('Skill upload is not available (not logged in?)');
            return;
        }
        this.deps.showStatus('Uploading local skills to cloud...');
        this.deps.requestRender();
        try {
            const result = await this.deps.uploadSkills(async (request) => {
                const totalFiles = request.modifiedFiles + request.localOnlyFiles + request.remoteOnlyFiles;
                const details = request.kind === 'create'
                    ? [
                        'Create this skill in cloud from the local folder.',
                        `This skill contains ${request.localOnlyFiles} ${this.pluralizeFile(request.localOnlyFiles)}.`
                    ]
                    : [
                        'Replace the cloud copy with the local folder.',
                        `This will change ${totalFiles} ${this.pluralizeFile(totalFiles)} in cloud.`
                    ];
                details.push(...this.formatTimestampDetails(request.localModifiedAt, request.cloudModifiedAt));
                details.push(...this.formatFileDiffDetails(request.fileDiffs, 'upload'));
                return this.deps.showConfirm(`Skill "${request.skillName}"`, details.join('\n'));
            });
            const lines = [];
            if (result.uploaded.length > 0) {
                lines.push(`  ${theme.fg('success', 'Uploaded:')}`);
                for (const name of result.uploaded) {
                    lines.push(`    ${theme.fg('accent', name)}`);
                    const change = result.changes?.find((item) => item.skillName === name);
                    if (change) {
                        lines.push(...this.formatRenderedChange(change, 'upload'));
                    }
                }
            }
            if (result.skipped.length > 0) {
                lines.push(`  ${theme.fg('dim', 'Unchanged:')}`);
                for (const name of result.skipped) {
                    lines.push(`    ${theme.fg('dim', `UM ${name}`)}`);
                }
            }
            if (result.skippedByUser.length > 0) {
                lines.push(`  ${theme.fg('dim', `Skipped: ${result.skippedByUser.join(', ')}`)}`);
            }
            if (result.blockedTeam.length > 0) {
                lines.push(`  ${theme.fg('warning', 'Team Skills Not Uploaded:')}`);
                for (const name of result.blockedTeam) {
                    lines.push(`    ${theme.fg('warning', name)}`);
                }
            }
            if (result.errors.length > 0) {
                for (const error of result.errors) {
                    lines.push(`  ${theme.fg('error', error)}`);
                }
            }
            if (lines.length === 0) {
                lines.push('  No upload changes.');
            }
            lines.push(`  ${theme.fg('dim', this.fileDiffLegend())}`);
            this.renderSummary('Skills Upload', lines);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            this.deps.showWarning(`Skill upload failed: ${message}`);
        }
    }
    renderSummary(title, lines) {
        this.deps.chatContainer.addChild(new Spacer(1));
        this.deps.chatContainer.addChild(new DynamicBorder());
        this.deps.chatContainer.addChild(new Text(theme.bold(theme.fg('accent', title)), 1, 0));
        this.deps.chatContainer.addChild(new Text(lines.join('\n'), 0, 0));
        this.deps.chatContainer.addChild(new Spacer(1));
        this.deps.chatContainer.addChild(new DynamicBorder());
        this.deps.requestRender();
    }
    pluralizeFile(count) {
        return count === 1 ? 'file' : 'files';
    }
    formatTimestampDetails(localModifiedAt, cloudModifiedAt) {
        const lines = [];
        if (localModifiedAt) {
            lines.push(`Local modified: ${this.formatTimestamp(localModifiedAt)}`);
        }
        if (cloudModifiedAt) {
            lines.push(`Cloud modified: ${this.formatTimestamp(cloudModifiedAt)}`);
        }
        return lines;
    }
    formatFileDiffDetails(fileDiffs, direction) {
        if (fileDiffs.length === 0) {
            return [];
        }
        return [
            'Files:',
            ...fileDiffs.map((diff) => `  ${this.formatFileDiff(diff, direction)}`),
            this.fileDiffLegend()
        ];
    }
    formatRenderedChange(change, direction) {
        const lines = this.formatTimestampDetails(change.localModifiedAt, change.cloudModifiedAt).map((line) => `      ${theme.fg('dim', line)}`);
        lines.push(...change.fileDiffs.map((diff) => `      ${theme.fg('dim', this.formatFileDiff(diff, direction))}`));
        return lines;
    }
    formatFileDiff(diff, direction) {
        if (diff.change === 'modified') {
            return `M ${diff.path}`;
        }
        if (direction === 'upload') {
            return diff.change === 'local-only' ? `A ${diff.path}` : `D ${diff.path}`;
        }
        return diff.change === 'remote-only' ? `A ${diff.path}` : `D ${diff.path}`;
    }
    fileDiffLegend() {
        return 'Legend: A added, M modified, D deleted, UM unchanged';
    }
    formatTimestamp(value) {
        const date = new Date(value);
        if (Number.isNaN(date.getTime())) {
            return value;
        }
        return date.toLocaleString();
    }
    getSkillDisplaySource(filePath, source) {
        const inferred = inferExternalSkillProviderFromPath(filePath);
        if (inferred) {
            return inferred;
        }
        const bundledRoot = getBundledSkillsDir();
        if (filePath.startsWith(bundledRoot)) {
            return 'built-in';
        }
        return source;
    }
}
//# sourceMappingURL=skills-workflow.js.map