/**
 * OS-native secret storage.
 *
 * macOS: Keychain via `security` CLI (always available).
 * Windows: Credential Manager via PowerShell + advapi32 P/Invoke (always available, no extra modules).
 *
 * Secrets are stored as JSON payloads keyed by connection name.
 *
 * Imports: none from internal layers. Pure shell-out utility.
 */
import { execSync } from 'child_process';
const CONNECTION_SERVICE = 'shortcut.connection';
// =============================================================================
// Public API
// =============================================================================
/** Store a secret payload for a connection. Overwrites if exists. */
export function setSecret(name, payload) {
    setNamespacedSecret(CONNECTION_SERVICE, name, payload);
}
/** Retrieve a secret payload for a connection. Returns undefined if not found. */
export function getSecret(name) {
    return getNamespacedSecret(CONNECTION_SERVICE, name);
}
/** Remove a secret for a connection. Returns true if it existed. */
export function removeSecret(name) {
    return removeNamespacedSecret(CONNECTION_SERVICE, name);
}
/** Store a JSON payload in the OS keychain under a custom service namespace. */
export function setNamespacedSecret(service, name, payload) {
    const json = JSON.stringify(payload);
    if (process.platform === 'darwin') {
        execSync(`security add-generic-password -a ${esc(name)} -s ${esc(service)} -w ${esc(json)} -U`, { stdio: 'ignore', timeout: 5000 });
    }
    else if (process.platform === 'win32') {
        winCredWrite(`${service}/${name}`, json);
    }
    else {
        throw new Error(`Unsupported platform: ${process.platform}. Secrets require macOS or Windows.`);
    }
}
/** Retrieve a JSON payload from the OS keychain for a custom service namespace. */
export function getNamespacedSecret(service, name) {
    try {
        if (process.platform === 'darwin') {
            const raw = execSync(`security find-generic-password -a ${esc(name)} -s ${esc(service)} -w`, {
                encoding: 'utf-8',
                stdio: ['ignore', 'pipe', 'ignore'],
                timeout: 5000
            });
            return JSON.parse(raw.trim());
        }
        else if (process.platform === 'win32') {
            const raw = winCredRead(`${service}/${name}`);
            if (!raw)
                return undefined;
            return JSON.parse(raw);
        }
    }
    catch {
        return undefined;
    }
    return undefined;
}
/** Remove a JSON payload from the OS keychain for a custom service namespace. */
export function removeNamespacedSecret(service, name) {
    try {
        if (process.platform === 'darwin') {
            execSync(`security delete-generic-password -a ${esc(name)} -s ${esc(service)}`, {
                stdio: 'ignore',
                timeout: 5000
            });
            return true;
        }
        else if (process.platform === 'win32') {
            return winCredDelete(`${service}/${name}`);
        }
    }
    catch {
        return false;
    }
    return false;
}
// =============================================================================
// Env var helpers
// =============================================================================
/**
 * Build environment variables for a set of connections.
 * Returns a Record to merge into a subprocess's env.
 *
 * Field names are used as-is for env var keys — name your fields to match
 * what the target tool expects (e.g. GITHUB_TOKEN, DATABASE_URL).
 */
export function buildConnectionEnv(connectionNames) {
    const missing = [];
    const env = {};
    for (const name of connectionNames) {
        const secret = getSecret(name);
        if (!secret) {
            missing.push(name);
            continue;
        }
        for (const [field, value] of Object.entries(secret)) {
            env[field] = value;
        }
    }
    if (missing.length) {
        throw new Error(`Credentials not found for connection(s): ${missing.join(', ')}. Run /connect to set them up.`);
    }
    return env;
}
// =============================================================================
// Windows Credential Manager (advapi32 P/Invoke — stock Windows, no modules)
// =============================================================================
/**
 * PowerShell script that uses P/Invoke to call CredRead/CredWrite/CredDelete
 * from advapi32.dll. Works on every Windows version. No external modules needed.
 */
const WIN_CRED_PREAMBLE = `
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.Text;
public class CredManager {
    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
    public static extern bool CredRead(string target, int type, int flags, out IntPtr cred);
    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
    public static extern bool CredWrite(ref CREDENTIAL cred, int flags);
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool CredDelete(string target, int type, int flags);
    [DllImport("advapi32.dll")]
    public static extern void CredFree(IntPtr cred);
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
    public struct CREDENTIAL {
        public int Flags; public int Type; public string TargetName; public string Comment;
        public long LastWritten; public int CredentialBlobSize; public IntPtr CredentialBlob;
        public int Persist; public int AttributeCount; public IntPtr Attributes;
        public string TargetAlias; public string UserName;
    }
    public static string Read(string target) {
        IntPtr ptr; if (!CredRead(target, 1, 0, out ptr)) return null;
        try {
            CREDENTIAL c = (CREDENTIAL)Marshal.PtrToStructure(ptr, typeof(CREDENTIAL));
            return Marshal.PtrToStringUni(c.CredentialBlob, c.CredentialBlobSize / 2);
        } finally { CredFree(ptr); }
    }
    public static void Write(string target, string secret) {
        byte[] bytes = Encoding.Unicode.GetBytes(secret);
        CREDENTIAL c = new CREDENTIAL();
        c.Type = 1; c.TargetName = target; c.CredentialBlobSize = bytes.Length;
        c.Persist = 2; c.UserName = target;
        IntPtr blob = Marshal.AllocHGlobal(bytes.Length);
        try { Marshal.Copy(bytes, 0, blob, bytes.Length); c.CredentialBlob = blob;
            if (!CredWrite(ref c, 0)) throw new Exception("CredWrite failed: " + Marshal.GetLastWin32Error());
        } finally { Marshal.FreeHGlobal(blob); }
    }
    public static bool Delete(string target) { return CredDelete(target, 1, 0); }
}
"@
`.trim();
/** Encode a PowerShell script as Base64 UTF-16LE for -EncodedCommand. */
function psEncode(script) {
    return Buffer.from(script, 'utf16le').toString('base64');
}
function winCredWrite(target, secret) {
    const ps = `${WIN_CRED_PREAMBLE}\n[CredManager]::Write('${psEsc(target)}', '${psEsc(secret)}')`;
    execSync(`powershell -NoProfile -EncodedCommand ${psEncode(ps)}`, {
        stdio: 'ignore',
        timeout: 10000
    });
}
function winCredRead(target) {
    const ps = `${WIN_CRED_PREAMBLE}\n$r = [CredManager]::Read('${psEsc(target)}'); if ($r) { Write-Output $r }`;
    const raw = execSync(`powershell -NoProfile -EncodedCommand ${psEncode(ps)}`, {
        encoding: 'utf-8',
        stdio: ['ignore', 'pipe', 'ignore'],
        timeout: 10000
    });
    const trimmed = raw.trim();
    return trimmed || undefined;
}
function winCredDelete(target) {
    const ps = `${WIN_CRED_PREAMBLE}\n[CredManager]::Delete('${psEsc(target)}')`;
    try {
        execSync(`powershell -NoProfile -EncodedCommand ${psEncode(ps)}`, {
            stdio: 'ignore',
            timeout: 10000
        });
        return true;
    }
    catch {
        return false;
    }
}
// =============================================================================
// Validation
// =============================================================================
/** Only alphanumeric, hyphens, underscores, and dots. Prevents shell flag injection and broken env var names. */
const SAFE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
export function validateConnectionName(name) {
    if (!SAFE_NAME_RE.test(name))
        return 'Must start with a letter/digit and contain only letters, digits, hyphens, underscores, or dots.';
    if (name.length > 128)
        return 'Must be 128 characters or fewer.';
    return undefined;
}
export function validateFieldName(name) {
    if (!SAFE_NAME_RE.test(name))
        return 'Must start with a letter/digit and contain only letters, digits, hyphens, underscores, or dots.';
    if (name.length > 64)
        return 'Must be 64 characters or fewer.';
    return undefined;
}
// =============================================================================
// Helpers
// =============================================================================
/** Shell-escape for POSIX (macOS security CLI). */
function esc(s) {
    return `'${s.replace(/'/g, "'\\''")}'`;
}
/** Escape for PowerShell single-quoted strings. */
function psEsc(s) {
    return s.replace(/'/g, "''");
}
//# sourceMappingURL=secret-store.js.map