import { buildRegistry } from "./registry";
import { logDeprecation } from "./utils/logging";
import { appsPlugin } from "./plugins/apps/index";
import { fetchPlugin } from "./plugins/fetch/index";
import { apiPlugin } from "./plugins/api/index";
import { getProfilePlugin } from "./plugins/getProfile";
import { listAppsPlugin } from "./plugins/listApps";
import { getAppPlugin } from "./plugins/getApp";
import { listActionsPlugin } from "./plugins/listActions";
import { getActionPlugin } from "./plugins/getAction";
import { runActionPlugin } from "./plugins/runAction";
import { listConnectionsPlugin } from "./plugins/listConnections";
import { getConnectionPlugin } from "./plugins/getConnection";
import { findFirstConnectionPlugin } from "./plugins/findFirstConnection";
import { findUniqueConnectionPlugin } from "./plugins/findUniqueConnection";
import { listAuthenticationsPlugin, getAuthenticationPlugin, findFirstAuthenticationPlugin, findUniqueAuthenticationPlugin, } from "./plugins/deprecated/authentications";
import { listClientCredentialsPlugin } from "./plugins/listClientCredentials";
import { createClientCredentialsPlugin } from "./plugins/createClientCredentials";
import { deleteClientCredentialsPlugin } from "./plugins/deleteClientCredentials";
import { listActionInputFieldsPlugin } from "./plugins/listActionInputFields";
import { getActionInputFieldsSchemaPlugin } from "./plugins/getActionInputFieldsSchema";
import { listActionInputFieldChoicesPlugin } from "./plugins/listActionInputFieldChoices";
import { listInputFieldsDeprecatedPlugin, getInputFieldsSchemaDeprecatedPlugin, listInputFieldChoicesDeprecatedPlugin, } from "./plugins/deprecated/inputFields";
// Trigger plugins are experimental — registered only by the
// `@zapier/zapier-sdk/experimental` factory. See `experimental.ts` and
// the "Experimental gating" section in
// `docs/design/2026-05-01-triggers-in-sdk.md`.
import { listTablesPlugin } from "./plugins/tables/listTables";
import { getTablePlugin } from "./plugins/tables/getTable";
import { deleteTablePlugin } from "./plugins/tables/deleteTable";
import { createTablePlugin } from "./plugins/tables/createTable";
import { listTableFieldsPlugin } from "./plugins/tables/listTableFields";
import { createTableFieldsPlugin } from "./plugins/tables/createTableFields";
import { deleteTableFieldsPlugin } from "./plugins/tables/deleteTableFields";
import { getTableRecordPlugin } from "./plugins/tables/getTableRecord";
import { listTableRecordsPlugin } from "./plugins/tables/listTableRecords";
import { createTableRecordsPlugin } from "./plugins/tables/createTableRecords";
import { deleteTableRecordsPlugin } from "./plugins/tables/deleteTableRecords";
import { updateTableRecordsPlugin } from "./plugins/tables/updateTableRecords";
import { requestPlugin } from "./plugins/request";
import { manifestPlugin } from "./plugins/manifest";
import { capabilitiesPlugin } from "./plugins/capabilities";
import { connectionsPlugin } from "./plugins/connections";
import { eventEmissionPlugin } from "./plugins/eventEmission";
export function createOptionsPlugin(options) {
    return () => ({ context: { options } });
}
export function createSdk() {
    return buildSdk({}, { meta: {} });
}
// context is separated from other properties because it gets frozen
// and its meta sub-property is deep-merged across plugins
function buildSdk(properties, context) {
    const frozenContext = Object.freeze(context);
    const registryCache = new Map();
    const sdk = {
        ...properties,
        get context() {
            return frozenContext;
        },
        getContext: () => frozenContext, // runtime compat shim, not in types
        getRegistry(options) {
            const cacheKey = options?.package ?? "__all__";
            if (!registryCache.has(cacheKey)) {
                registryCache.set(cacheKey, buildRegistry({
                    sdk: sdk,
                    meta: frozenContext.meta,
                    packageFilter: options?.package,
                }));
            }
            return registryCache.get(cacheKey);
        },
        addPlugin(plugin, options) {
            const pluginResult = plugin({
                ...properties,
                context: frozenContext,
            });
            const { context: pluginContext, ...pluginProperties } = pluginResult;
            const { meta: pluginMeta, ...pluginContextRest } = pluginContext ?? {};
            const existingProperties = properties;
            const existingContext = context;
            const existingMeta = context.meta ?? {};
            // Detect collisions on the two shapes that matter: root-level
            // methods and non-meta context fields. Meta entries don't need
            // their own check — by convention a method comes with its meta,
            // so a method-bearing meta collision is already caught by the
            // root-key loop above. Pure meta-only overrides (e.g.
            // cliOverridesPlugin tagging `meta.fetch.deprecated`) stay
            // legitimate and last-win because their pluginProperties is
            // empty, so no method collision fires.
            //
            // Callers that genuinely intend to wrap an existing method (e.g.,
            // the CLI's drainTriggerInbox plugin replacing the SDK's same-named
            // method with a flag-aware wrapper) opt in via `{ override: true }`.
            // Plugin functions still execute against the pre-merge sdk, so
            // wrappers can close over the original implementation.
            const collisions = [];
            for (const key of Object.keys(pluginProperties)) {
                if (key in existingProperties)
                    collisions.push(`method "${key}"`);
            }
            for (const key of Object.keys(pluginContextRest)) {
                if (key in existingContext)
                    collisions.push(`context.${key}`);
            }
            if (collisions.length > 0 && !options?.override) {
                const name = plugin.name || "anonymous plugin";
                console.warn(`[zapier-sdk] Skipping "${name}" — duplicate registration of: ${collisions.join(", ")}. ` +
                    `If the duplicate is intentional, pass { override: true } to addPlugin or wrap with composePlugins(...). ` +
                    `Otherwise rename the method or remove the duplicate.`);
                // Whole-plugin skip: return the chain unchanged. The type
                // assertion lies about the skipped contributions; getRegistry-based
                // consumers (CLI, MCP) see an unchanged surface, and direct
                // typed callers will see TypeError on the missing method (the
                // upstream warn names the cause).
                return buildSdk(properties, context);
            }
            const mergedProperties = { ...properties, ...pluginProperties };
            const mergedContext = {
                ...context,
                ...pluginContextRest,
                meta: { ...existingMeta, ...(pluginMeta ?? {}) },
            };
            return buildSdk(mergedProperties, mergedContext);
        },
    };
    return sdk;
}
/**
 * @deprecated `getRegistry` is now built into every sdk, so the "without
 * registry" form has no behavioral difference from {@link createZapierSdk}.
 * Use {@link createZapierSdk} instead.
 */
export function createZapierSdkWithoutRegistry(options = {}) {
    logDeprecation("createZapierSdkWithoutRegistry is deprecated; use createZapierSdk instead. getRegistry is now available on every sdk.");
    return createZapierSdk(options);
}
export function createZapierSdk(options = {}) {
    return (createSdk()
        .addPlugin(createOptionsPlugin(options))
        // Event emission (must be first to be available to other plugins)
        .addPlugin(eventEmissionPlugin)
        // Provides the API client in context
        .addPlugin(apiPlugin)
        // Manifest plugin (provides version locking context) - must come after apiPlugin
        .addPlugin(manifestPlugin)
        // Capabilities gates (must come after manifestPlugin for config access)
        .addPlugin(capabilitiesPlugin)
        // Named connections resolution
        .addPlugin(connectionsPlugin)
        // Apps/actions/fields
        .addPlugin(listAppsPlugin)
        .addPlugin(getAppPlugin)
        .addPlugin(listActionsPlugin)
        .addPlugin(getActionPlugin)
        .addPlugin(listActionInputFieldsPlugin)
        .addPlugin(getActionInputFieldsSchemaPlugin)
        .addPlugin(listActionInputFieldChoicesPlugin)
        // Deprecated aliases for the unprefixed names — delegate to the *Action*
        // plugins above and stay hidden from generated docs / CLI help.
        .addPlugin(listInputFieldsDeprecatedPlugin)
        .addPlugin(getInputFieldsSchemaDeprecatedPlugin)
        .addPlugin(listInputFieldChoicesDeprecatedPlugin)
        // Run action
        .addPlugin(runActionPlugin)
        // Connections
        .addPlugin(listConnectionsPlugin)
        .addPlugin(getConnectionPlugin)
        .addPlugin(findFirstConnectionPlugin)
        .addPlugin(findUniqueConnectionPlugin)
        // Deprecated authentication plugins (thin wrappers for backward compatibility)
        .addPlugin(listAuthenticationsPlugin)
        .addPlugin(getAuthenticationPlugin)
        .addPlugin(findFirstAuthenticationPlugin)
        .addPlugin(findUniqueAuthenticationPlugin)
        // Client Credentials
        .addPlugin(listClientCredentialsPlugin)
        .addPlugin(createClientCredentialsPlugin)
        .addPlugin(deleteClientCredentialsPlugin)
        // Relay fetching — fetch is the primary HTTP plugin; request is a deprecated wrapper
        .addPlugin(fetchPlugin)
        .addPlugin(requestPlugin)
        // Triggers (experimental — registered in src/experimental.ts only)
        // Tables
        .addPlugin(listTablesPlugin)
        .addPlugin(getTablePlugin)
        .addPlugin(deleteTablePlugin)
        .addPlugin(createTablePlugin)
        .addPlugin(listTableFieldsPlugin)
        .addPlugin(createTableFieldsPlugin)
        .addPlugin(deleteTableFieldsPlugin)
        .addPlugin(getTableRecordPlugin)
        .addPlugin(listTableRecordsPlugin)
        .addPlugin(createTableRecordsPlugin)
        .addPlugin(deleteTableRecordsPlugin)
        .addPlugin(updateTableRecordsPlugin)
        // Magic apps plugin
        .addPlugin(appsPlugin)
        // Profile
        .addPlugin(getProfilePlugin));
}
