/**
 * /connect slash command.
 *
 * Manages external service connections.
 * Metadata (name, field names) → settings.json via SettingsManager.
 * Secret values → OS keychain via secret-store.
 *
 * Connections are free-form key-value pairs. No predefined types or schemas.
 */
import { getSettingsPath } from '../../config.js';
import { formatOpenError, openInEditor } from '../../shared/platform/open.js';
import { getSecret, removeSecret, setSecret, validateConnectionName, validateFieldName } from './secret-store.js';
/** Top-level interactive loop for `/connect`: lists, adds, edits, and removes named connections. */
export async function handleConnectCommand(showStatus, select, input, settingsManager) {
    while (true) {
        const connections = settingsManager.getConnections();
        const names = Object.keys(connections);
        const options = [
            '+ Add connection',
            ...names.map((n) => {
                const fields = connections[n]?.fields.join(', ') ?? '';
                return `  ${n} (${fields})`;
            }),
            '---',
            'Open config',
            'Cancel'
        ];
        const choice = await select('Connections', options);
        if (!choice || choice === 'Cancel')
            return true;
        if (choice === '+ Add connection') {
            await addConnection(showStatus, select, input, settingsManager);
        }
        else if (choice === 'Open config') {
            const configPath = getSettingsPath();
            void openInEditor(configPath).catch((error) => showStatus(`Could not open ${configPath}: ${formatOpenError(error)}`));
            showStatus(`Opened ${configPath}`);
        }
        else {
            const name = choice.replace(/^\s+/, '').replace(/\s*\(.*\)$/, '');
            await manageConnection(name, showStatus, select, input, settingsManager);
        }
    }
}
/** Prompt for a name and a list of fields, then persist a new connection plus its keychain payload. */
async function addConnection(showStatus, _select, input, sm) {
    const name = await input('Connection name', 'snowflake-prod');
    if (!name?.trim())
        return;
    const trimmedName = name.trim();
    const nameErr = validateConnectionName(trimmedName);
    if (nameErr) {
        showStatus(`Invalid connection name: ${nameErr}`);
        return;
    }
    if (sm.getConnection(trimmedName)) {
        showStatus(`Connection "${trimmedName}" already exists. Remove it first.`);
        return;
    }
    showStatus('Enter fields one at a time. Leave blank to finish.');
    const payload = {};
    const fieldNames = [];
    const seenFields = new Set();
    while (true) {
        const fieldName = await input('Field name (blank to finish)', '');
        if (!fieldName?.trim())
            break;
        const field = fieldName.trim();
        const fieldErr = validateFieldName(field);
        if (fieldErr) {
            showStatus(`Invalid field name "${field}": ${fieldErr}`);
            continue;
        }
        if (seenFields.has(field)) {
            showStatus(`Duplicate field "${field}" — skipping.`);
            continue;
        }
        seenFields.add(field);
        const value = await input(field, '');
        if (value === undefined)
            return;
        if (value.trim()) {
            payload[field] = value.trim();
        }
        fieldNames.push(field);
    }
    if (fieldNames.length === 0) {
        showStatus('No fields entered. Connection not created.');
        return;
    }
    const metadata = { fields: fieldNames };
    sm.setConnection(trimmedName, metadata);
    try {
        setSecret(trimmedName, payload);
        showStatus(`Connection "${trimmedName}" saved — credentials stored in OS keychain`);
    }
    catch (err) {
        sm.removeConnection(trimmedName);
        showStatus(`Failed to store credentials: ${err instanceof Error ? err.message : String(err)}`);
    }
}
/** Sub-menu for an existing connection: edit, add a field, or remove it entirely. */
async function manageConnection(name, showStatus, select, input, sm) {
    while (true) {
        const meta = sm.getConnection(name);
        if (!meta)
            return; // Connection was removed
        const secret = getSecret(name);
        const options = ['Edit fields', 'Add field', 'Remove', 'Back'];
        const choice = await select(`Connection: ${name}`, options);
        if (!choice || choice === 'Back')
            return;
        if (choice === 'Edit fields') {
            await editConnectionFields(name, meta, secret, showStatus, select, input, sm);
        }
        else if (choice === 'Add field') {
            await addFieldToConnection(name, meta, secret, showStatus, input, sm);
        }
        else if (choice === 'Remove') {
            const confirm = await select(`Remove "${name}"? This deletes credentials from the OS keychain.`, ['Remove', 'Cancel']);
            if (confirm === 'Remove') {
                sm.removeConnection(name);
                removeSecret(name);
                showStatus(`Connection "${name}" removed`);
                return; // Connection gone, back to top-level
            }
        }
    }
}
/** Pick one field on an existing connection and update its keychain value (blank keeps current). */
async function editConnectionFields(name, meta, secret, showStatus, select, input, _sm) {
    const fieldOptions = meta.fields.map((f) => {
        const val = secret?.[f];
        const preview = val ? maskValue(val) : '(empty)';
        return `${f}: ${preview}`;
    });
    fieldOptions.push('Cancel');
    const fieldChoice = await select(`Edit field — ${name}`, fieldOptions);
    if (!fieldChoice || fieldChoice === 'Cancel')
        return;
    const fieldName = fieldChoice.replace(/:\s.*$/, '');
    const currentVal = secret?.[fieldName] ?? '';
    const newVal = await input(`${fieldName} (blank to keep current)`, currentVal);
    if (newVal === undefined)
        return;
    const trimmed = newVal.trim();
    const updatedPayload = { ...secret };
    if (trimmed) {
        updatedPayload[fieldName] = trimmed;
    }
    // If blank, keep the existing value unchanged
    try {
        setSecret(name, updatedPayload);
        if (trimmed) {
            showStatus(`Updated "${fieldName}" for connection "${name}"`);
        }
        else {
            showStatus(`"${fieldName}" unchanged`);
        }
    }
    catch (err) {
        showStatus(`Failed to update: ${err instanceof Error ? err.message : String(err)}`);
    }
}
/** Append a new field to an existing connection, rolling back metadata on keychain failure. */
async function addFieldToConnection(name, meta, secret, showStatus, input, sm) {
    const fieldName = await input('New field name', '');
    if (!fieldName?.trim())
        return;
    const field = fieldName.trim();
    const fieldErr = validateFieldName(field);
    if (fieldErr) {
        showStatus(`Invalid field name "${field}": ${fieldErr}`);
        return;
    }
    if (meta.fields.includes(field)) {
        showStatus(`Field "${field}" already exists. Use "Edit fields" to update it.`);
        return;
    }
    const value = await input(field, '');
    if (value === undefined)
        return;
    const updatedFields = [...meta.fields, field];
    sm.setConnection(name, { fields: updatedFields });
    const updatedPayload = { ...secret };
    if (value.trim()) {
        updatedPayload[field] = value.trim();
    }
    try {
        setSecret(name, updatedPayload);
        showStatus(`Added field "${field}" to connection "${name}"`);
    }
    catch (err) {
        // Roll back the metadata change
        sm.setConnection(name, meta);
        showStatus(`Failed to store credentials: ${err instanceof Error ? err.message : String(err)}`);
    }
}
/** Mask a secret value for display, showing only the first and last 2 chars. */
function maskValue(val) {
    if (val.length <= 6)
        return '••••••';
    return `${val.slice(0, 2)}${'•'.repeat(Math.min(val.length - 4, 8))}${val.slice(-2)}`;
}
//# sourceMappingURL=connect-command.js.map