/**
 * Phase 5a — Skill-source picker (olam-setup-epic Phase B).
 *
 * Three paths (in evaluation order):
 *   1. Skip-on-flag  — opts.skipSkillSource is true
 *   2. Non-interactive — opts.skillSource provides a curated name or git URL
 *   3. Interactive TUI — @inquirer/prompts select + optional custom-URL input
 *
 * After path 2 or 3 resolves a git URL: addSkillSource → cloneSkillSource →
 * appendTrustAudit('setup-skill-source-picked').
 *
 * TTY-safety: when !process.stdin.isTTY and no --skill-source flag, falls back
 * to the recommended default (atlas-toolbox) with a stderr hint — mirrors the
 * defaultPrompt pattern in setup.ts.
 *
 * Dependency injection: real @olam/core functions are lazily imported so this
 * module resolves in vitest without a pre-built @olam/core dist. Callers
 * inject stubs via RunSetupDeps for unit tests.
 */
import { select, input } from '@inquirer/prompts';
// ── Curated source list ────────────────────────────────────────────────
const CURATED_SOURCES = [
    {
        value: 'atlas-toolbox',
        name: 'atlas-toolbox (recommended)',
        gitUrl: 'https://github.com/grain-tech/atlas-toolbox.git',
    },
    {
        value: 'grain-tech/ai-skills',
        name: 'grain-tech/ai-skills',
        gitUrl: 'https://github.com/grain-tech/ai-skills.git',
    },
    {
        value: 'custom',
        name: 'Custom git URL…',
        gitUrl: null,
    },
];
const DEFAULT_SOURCE = CURATED_SOURCES[0];
// ── Loose git-URL validator ────────────────────────────────────────────
function isGitUrl(s) {
    return /^(https?:\/\/|git@).+\.git$/.test(s);
}
// ── Lazy real-impl loaders (dynamic import avoids vitest dist requirement) ──
async function loadAddSkillSource() {
    const m = await import('@olam/core/src/skill-sources/index.js');
    return m.addSkillSource;
}
async function loadCloneSkillSource() {
    const m = await import('@olam/core/src/skill-sources/index.js');
    return m.cloneSkillSource;
}
async function loadAppendTrustAudit() {
    const m = await import('@olam/core/src/skill-sources/index.js');
    return m.appendTrustAudit;
}
async function loadRedactUrl() {
    const m = await import('@olam/core/src/skill-sources/index.js');
    return m.redactUrl;
}
// ── Resolve deps (injected takes priority; lazy real otherwise) ────────
async function resolveSkillDeps(deps) {
    const addFn = deps.addSkillSourceFn ?? (await loadAddSkillSource());
    const cloneFn = deps.cloneSkillSourceFn ?? (await loadCloneSkillSource());
    const auditFn = deps.appendTrustAuditFn ?? (await loadAppendTrustAudit());
    const redactFn = deps.redactUrlFn ?? (await loadRedactUrl());
    const selectFn = (deps.selectPromptFn ?? select);
    const inputFn = (deps.inputPromptFn ?? input);
    return { addFn, cloneFn, auditFn, redactFn, selectFn, inputFn };
}
// ── Git URL resolution ─────────────────────────────────────────────────
async function resolveGitUrl(opts, selectFn, inputFn) {
    // Path 2: non-interactive via --skill-source flag
    if (opts.skillSource) {
        const s = opts.skillSource.trim();
        const matched = CURATED_SOURCES.find((c) => c.gitUrl === s || c.value === s);
        if (matched && matched.gitUrl !== null) {
            return { gitUrl: matched.gitUrl, name: matched.value, trustMethod: 'flag' };
        }
        // CP3 HIGH fix: non-interactive path must validate raw URLs the same way the
        // interactive `input` validator does. Without this, `--skill-source garbage`
        // would pass garbage through to addSkillSource and only fail downstream with
        // an opaque message. Throw a clear error here so the caller's catch surfaces
        // a remediation prompt naming the flag.
        if (!isGitUrl(s)) {
            throw new Error(`--skill-source value "${s}" is not a recognized curated name (atlas-toolbox, grain-tech/ai-skills) `
                + `and not a valid git URL (https://…/*.git or git@…:…/*.git).`);
        }
        const urlName = s.split('/').pop()?.replace(/\.git$/, '') ?? 'custom-source';
        return { gitUrl: s, name: urlName, trustMethod: 'flag' };
    }
    // Path 3: TTY-safety fallback
    if (!process.stdin.isTTY) {
        process.stderr.write(`[olam setup] non-TTY stdin — defaulting to ${DEFAULT_SOURCE.name}. `
            + `Pass \`--skill-source\` for explicit non-interactive selection.\n`);
        return {
            gitUrl: DEFAULT_SOURCE.gitUrl,
            name: DEFAULT_SOURCE.value,
            trustMethod: 'interactive',
        };
    }
    // Path 3: interactive TUI
    const choices = CURATED_SOURCES.map((s) => ({ value: s.value, name: s.name }));
    const picked = await selectFn({ message: 'Pick a skill source to add:', choices });
    const source = CURATED_SOURCES.find((s) => s.value === picked);
    if (source.gitUrl !== null) {
        return { gitUrl: source.gitUrl, name: source.value, trustMethod: 'interactive' };
    }
    // Custom URL sub-prompt
    const customUrl = await inputFn({
        message: 'Enter git URL (https:// or git@…):',
        validate: (v) => {
            if (isGitUrl(v))
                return true;
            return 'Must be a valid git URL (https://…/*.git or git@…:…/*.git)';
        },
    });
    const urlName = customUrl.split('/').pop()?.replace(/\.git$/, '') ?? 'custom-source';
    return { gitUrl: customUrl, name: urlName, trustMethod: 'interactive' };
}
// ── Exported phase function ────────────────────────────────────────────
export async function pickSkillSourcePhase(opts, deps) {
    // Path 1: skip-on-flag (defensive — caller already short-circuits, but keep idempotent)
    if (opts.skipSkillSource) {
        return { ok: true, skipped: true, message: 'skipped via --skip-skill-source' };
    }
    const { addFn, cloneFn, auditFn, redactFn, selectFn, inputFn } = await resolveSkillDeps(deps);
    let resolved;
    try {
        resolved = await resolveGitUrl(opts, selectFn, inputFn);
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        return {
            ok: false,
            message: `skill-source picker failed: ${msg}`,
            remedy: 'Re-run `olam setup` or use `--skill-source <git-url>` to bypass the picker.',
        };
    }
    let added;
    try {
        added = addFn({
            name: resolved.name,
            gitUrl: resolved.gitUrl,
            trustMethod: resolved.trustMethod,
        });
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        // CP3 MEDIUM fix: idempotent re-run. addSkillSource throws when a source with
        // the same gitUrl OR display name is already registered (store.ts:42-50).
        // Treat that as success-no-op so a second `olam setup` invocation doesn't
        // dead-end at Phase 5a with a non-recoverable "remedy: re-run" loop.
        if (/already registered|already in use/i.test(msg)) {
            return {
                ok: true,
                skipped: true,
                message: `${resolved.name} already registered (idempotent re-run)`,
            };
        }
        return {
            ok: false,
            message: `addSkillSource failed: ${msg}`,
            remedy: 'Run `olam skills source list` to inspect existing sources, then re-run `olam setup` after resolving.',
        };
    }
    try {
        cloneFn({ gitUrl: added.gitUrl, branch: added.branch, id: added.id });
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        return {
            ok: false,
            message: `clone failed for ${redactFn(added.gitUrl)}: ${msg}`,
            remedy: 'Check network + verify git URL; re-run `olam setup`.',
        };
    }
    try {
        // CP3 MEDIUM fix: redact basic-auth credentials before persisting to audit log.
        // A user could legitimately paste `https://user:token@github.com/...` into the
        // custom-URL prompt; the audit log is operator-readable on disk and must not
        // retain the secret. `redactUrl` (from @olam/core) strips `user:pass@`.
        auditFn({
            action: 'setup-skill-source-picked',
            gitUrl: redactFn(resolved.gitUrl),
            trustMethod: resolved.trustMethod === 'flag' ? 'flag' : 'interactive',
            sourceId: added.id,
        });
    }
    catch {
        // audit-log failures must not gate a successful phase
    }
    const shortId = added.id.slice(0, 8);
    return { ok: true, message: `registered ${added.name} (${shortId})` };
}
//# sourceMappingURL=setup-phase-5a-skill-source.js.map