/**
 * Managed-runtime emitter — V2 Phase 3, Round 2.
 *
 * Handles `topology: 'managed'` factories: external SaaS providers reached
 * via vendor-specific SDKs (Stripe, OpenAI, Anthropic, Twilio, AWS, etc.).
 *
 * For each `import:` entry the consumer declares, this emitter produces:
 *   clients/<provider-name>.ts   — SDK init + typed client
 *
 * No server-bind file — managed providers ship their own server.
 *
 * Auth dispatch (factory.v2metadata?.authentication?.kind):
 *   'api-key'  → `new SDK(process.env.<ENV>!)` constructor pattern (Stripe-shape)
 *   'bearer'   → header-based init reading token from env
 *   'oauth2'   → TODO scaffolding; init throws at call-time (Round 3)
 *   'mtls'     → env-driven cert paths
 *   'none'     → bare SDK init, no auth wiring
 *
 * Env var name: factory.v2metadata?.authentication?.env if present, else
 * derived from provider (STRIPE_API_KEY, OPENAI_API_KEY, etc.). Suffix
 * varies by auth.kind (_API_KEY / _TOKEN / _BEARER / _CERT).
 *
 * Operation routing: dotted ops like `Charge.create` route to
 * `sdk.charges.create(...)` — entity head is lowercased + naively
 * pluralised; tail becomes the SDK method. Exact mapping varies per
 * SDK; Round 3 enhancement allows spec authors to override.
 *
 * Deployment hint: realize emits a `deployments.<env>.instances.infrastructure.<provider>`
 * entry with `config: { provider: <hostname>, authEnv: <ENV_NAME> }` —
 * mentioned in result.notes.
 */
// ─── Naming helpers (shared shape with library.ts) ───────────────────────────
function parseSelectEntry(entry) {
    const dotIndex = entry.indexOf('.');
    if (dotIndex === -1)
        return { head: entry, tail: undefined };
    return { head: entry.slice(0, dotIndex), tail: entry.slice(dotIndex + 1) };
}
function toWords(fromName) {
    const normalised = fromName.replace(/^@/, '').replace(/\//g, '-');
    return normalised
        .replace(/([a-z])([A-Z])/g, '$1 $2')
        .replace(/[^a-zA-Z0-9]+/g, ' ')
        .trim()
        .split(' ')
        .filter(Boolean)
        .map((w) => w.toLowerCase());
}
function toClientName(fromName) {
    const words = toWords(fromName);
    if (words.length === 0)
        return 'unknownClient';
    return (words[0] +
        words.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(''));
}
function toInterfaceName(fromName) {
    return toWords(fromName)
        .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
        .join('');
}
function toFileName(fromName) {
    return toWords(fromName).join('-');
}
function resolveAuth(ctx) {
    const auth = ctx.factory.v2metadata?.authentication;
    const kind = auth?.kind ?? 'api-key';
    const upperProvider = toWords(ctx.importEntry.from).join('_').toUpperCase();
    let envVar;
    if (auth?.env && typeof auth.env === 'string') {
        envVar = auth.env;
    }
    else {
        const suffix = kind === 'bearer' ? '_TOKEN'
            : kind === 'oauth2' ? '_CLIENT_SECRET'
                : kind === 'mtls' ? '_CERT'
                    : kind === 'none' ? ''
                        : '_API_KEY';
        envVar = upperProvider + suffix;
    }
    return {
        kind,
        envVar,
        certEnvVar: auth?.certEnv,
        keyEnvVar: auth?.keyEnv,
    };
}
function resolveSdkSpecifier(ctx) {
    const sdk = ctx.factory.v2metadata?.sdk;
    return sdk ?? ctx.importEntry.from;
}
/**
 * Naive pluralise — appends `s` unless the word already ends in `s`.
 * SDK method conventions vary (charges, customers, invoices); spec authors
 * who need a different mapping can override per-call. Round 3 enhancement.
 */
function pluraliseLower(name) {
    const lower = name.toLowerCase();
    return lower.endsWith('s') ? lower : `${lower}s`;
}
// ─── Auth init pattern generators ─────────────────────────────────────────────
function generateAuthInit(sdkSpecifier, sdkVarName, auth) {
    const lines = [];
    switch (auth.kind) {
        case 'api-key':
            lines.push(`import SDK from '${sdkSpecifier}';`);
            lines.push('');
            lines.push(`const apiKey = process.env.${auth.envVar};`);
            lines.push(`if (!apiKey) throw new Error('${auth.envVar} env var is not set; required for ${sdkSpecifier} client');`);
            lines.push(`const ${sdkVarName} = new SDK(apiKey);`);
            break;
        case 'bearer':
            lines.push(`import SDK from '${sdkSpecifier}';`);
            lines.push('');
            lines.push(`const bearerToken = process.env.${auth.envVar};`);
            lines.push(`if (!bearerToken) throw new Error('${auth.envVar} env var is not set; required for ${sdkSpecifier} client');`);
            lines.push(`const ${sdkVarName} = new SDK({`);
            lines.push(`  headers: { Authorization: \`Bearer \${bearerToken}\` },`);
            lines.push(`});`);
            break;
        case 'oauth2':
            lines.push(`import SDK from '${sdkSpecifier}';`);
            lines.push('');
            lines.push(`// TODO (Round 3): wire OAuth2 client-credentials / authorization-code flow.`);
            lines.push(`//   Round 2 leaves this as a clear scaffolding stub. Configure the client_id`);
            lines.push(`//   and client_secret env vars + token endpoint, then exchange for a bearer token.`);
            lines.push(`function init${sdkVarName.charAt(0).toUpperCase() + sdkVarName.slice(1)}(): never {`);
            lines.push(`  throw new Error('OAuth2 wiring is a Round 3 TODO for ${sdkSpecifier} — supply token-exchange logic.');`);
            lines.push(`}`);
            lines.push(`const ${sdkVarName}: ReturnType<typeof init${sdkVarName.charAt(0).toUpperCase() + sdkVarName.slice(1)}> = init${sdkVarName.charAt(0).toUpperCase() + sdkVarName.slice(1)}();`);
            break;
        case 'mtls':
            lines.push(`import SDK from '${sdkSpecifier}';`);
            lines.push(`import { readFileSync } from 'node:fs';`);
            lines.push('');
            lines.push(`const certPath = process.env.${auth.certEnvVar ?? auth.envVar};`);
            lines.push(`const keyPath = process.env.${auth.keyEnvVar ?? auth.envVar.replace(/_CERT$/, '_KEY')};`);
            lines.push(`if (!certPath || !keyPath) throw new Error('mTLS cert/key env vars not set; required for ${sdkSpecifier} client');`);
            lines.push(`const ${sdkVarName} = new SDK({`);
            lines.push(`  cert: readFileSync(certPath),`);
            lines.push(`  key: readFileSync(keyPath),`);
            lines.push(`});`);
            break;
        case 'none':
            lines.push(`import SDK from '${sdkSpecifier}';`);
            lines.push('');
            lines.push(`const ${sdkVarName} = new SDK();`);
            break;
    }
    return lines;
}
// ─── Interface + constant generators ──────────────────────────────────────────
function generateInterface(interfaceName, selectEntries) {
    const lines = [];
    lines.push(`export type ${interfaceName}Client = {`);
    const seen = new Set();
    for (const entry of selectEntries) {
        if (seen.has(entry))
            continue;
        seen.add(entry);
        const { head, tail } = parseSelectEntry(entry);
        if (tail === undefined) {
            lines.push(`  /** ${head} entity — passthrough to SDK. Round 3: substitute real type. */`);
            lines.push(`  ${head}(...args: unknown[]): Promise<unknown>;`);
        }
        else {
            lines.push(`  /** ${head}.${tail} → sdk.${pluraliseLower(head)}.${tail}. Round 3: substitute real input/output types. */`);
            lines.push(`  ${tail}(input: unknown): Promise<unknown>;`);
        }
    }
    lines.push('};');
    return lines.join('\n');
}
function generateClientConstant(clientName, interfaceName, sdkVarName, selectEntries, auth) {
    const lines = [];
    lines.push(`export const ${clientName}Client: ${interfaceName}Client = {`);
    const seen = new Set();
    for (const entry of selectEntries) {
        if (seen.has(entry))
            continue;
        seen.add(entry);
        const { head, tail } = parseSelectEntry(entry);
        if (tail === undefined) {
            // Bare entity — passthrough to a top-level SDK property of that name.
            lines.push(`  ${head}(...args: unknown[]): Promise<unknown> {`);
            if (auth.kind === 'oauth2') {
                lines.push(`    throw new Error('${head}: OAuth2 wiring is a Round 3 TODO');`);
            }
            else {
                lines.push(`    return Promise.resolve(((${sdkVarName} as unknown) as Record<string, (...a: unknown[]) => unknown>)['${head}']?.(...args));`);
            }
            lines.push(`  },`);
        }
        else {
            // Dotted op — route to sdk.<plural-head>.<tail>.
            const sdkProp = pluraliseLower(head);
            lines.push(`  ${tail}(input: unknown): Promise<unknown> {`);
            if (auth.kind === 'oauth2') {
                lines.push(`    throw new Error('${head}.${tail}: OAuth2 wiring is a Round 3 TODO');`);
            }
            else {
                lines.push(`    const sdkAny = (${sdkVarName} as unknown) as Record<string, Record<string, (input: unknown) => Promise<unknown>>>;`);
                lines.push(`    const fn = sdkAny['${sdkProp}']?.['${tail}'];`);
                lines.push(`    if (typeof fn !== 'function') throw new Error('${sdkProp}.${tail} not found on SDK; check the SDK shape or override the mapping in the spec.');`);
                lines.push(`    return fn(input);`);
            }
            lines.push(`  },`);
        }
    }
    lines.push('};');
    return lines.join('\n');
}
// ─── Managed emitter ──────────────────────────────────────────────────────────
/**
 * Managed-runtime emitter implementation.
 *
 * One call to `emit()` processes a single `import:` entry. Produces:
 *   - 1 client file under `clients/<provider-filename>.ts`
 *   - NO server-bind file (managed providers ship their own server).
 */
export class ManagedRuntimeEmitter {
    topology = 'managed';
    async emit(ctx) {
        const { importEntry, factory } = ctx;
        const selectEntries = importEntry.select ?? [];
        const fileName = toFileName(importEntry.from);
        const clientName = toClientName(importEntry.from);
        const interfaceName = toInterfaceName(importEntry.from);
        const sdkSpecifier = resolveSdkSpecifier(ctx);
        const sdkVarName = `${clientName}Sdk`;
        const auth = resolveAuth(ctx);
        // Build header.
        const headerLines = [
            '// FACTORY-EMITTED — DO NOT EDIT (R8)',
            `// Generated by managed-runtime emitter — V2 Phase 3`,
            `// Provider: ${importEntry.from} (managed SaaS via factory: ${factory.name})`,
            `// SDK: ${sdkSpecifier}`,
            `// Authentication: ${auth.kind} (env: ${auth.envVar})`,
            '',
        ];
        const initLines = generateAuthInit(sdkSpecifier, sdkVarName, auth);
        const interfaceBlock = generateInterface(interfaceName, selectEntries);
        const constantBlock = generateClientConstant(clientName, interfaceName, sdkVarName, selectEntries, auth);
        const parts = [...headerLines, ...initLines, '', interfaceBlock, '', constantBlock, ''];
        const content = parts.join('\n');
        const consumerImport = {
            from: `./clients/${fileName}`,
            specifier: `${clientName}Client`,
        };
        const notes = [
            `Deployment hint: deployments.<env>.instances.infrastructure.${clientName} with config: { provider: '${importEntry.from}', authEnv: '${auth.envVar}' } (factory: ${factory.name}, auth: ${auth.kind}).`,
        ];
        if (selectEntries.length === 0) {
            notes.push(`Warning: import entry from '${importEntry.from}' has empty select: — no client members emitted.`);
        }
        if (auth.kind === 'oauth2') {
            notes.push(`TODO (Round 3): OAuth2 flow for ${importEntry.from} — supply token-exchange logic. Methods throw at call-time until wired.`);
        }
        const reviewNotes = [];
        if (auth.kind === 'oauth2') {
            reviewNotes.push(`OAuth2 flow for ${importEntry.from} — supply token-exchange logic; methods throw at call-time until wired.`);
        }
        const deploymentSuggestion = {
            provider: importEntry.from,
            consumerComponent: ctx.consumerComponent?.name,
            category: 'infrastructure',
            key: clientName,
            config: {
                provider: importEntry.from,
                authEnv: auth.envVar,
            },
            factory: factory.name,
            topology: 'managed',
            ...(reviewNotes.length > 0 ? { reviewNotes } : {}),
        };
        return {
            files: [{ path: `clients/${fileName}.ts`, content }],
            imports: [consumerImport],
            notes,
            deploymentSuggestion,
        };
    }
}
//# sourceMappingURL=managed.js.map