import z from "zod";
import { createToolFactory, ToolCategory, PermissionLevel, } from "../../tools2/types.js";
// All integration setup tools use ToolCategory.INTERACTIVE — they
// manipulate UI state (Redux form fields, tabs) while the user is
// looking at the screen. Combined with PermissionLevel.ALLOW for
// form/UI actions and save.
// ---------------------------------------------------------------------------
// openIntegrationSetup
// ---------------------------------------------------------------------------
export const openIntegrationSetupToolFactory = createToolFactory("openIntegrationSetup", ({ clark }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Opens the integration setup tab so the user can configure a new connection to a data source. " +
        "Use when the user needs to connect to a data source or set up a new integration. " +
        "If unsure of the pluginId, call listAvailableIntegrationTypes first to get the correct ID. " +
        "The tab will open in the UI and the user can fill in the required fields. " +
        "The response includes a formState with the exact field names, types, and required flags for this plugin. " +
        "IMPORTANT: If you already know any field values (host, port, database, name, region, etc.), " +
        "you MUST pass them in the `fields` parameter of this call. You will not be able to call " +
        "updateIntegrationFields in the same turn after opening. " +
        "Sensitive fields (passwords, API keys, tokens) will be ignored — the user must enter those directly in the form.",
    inputSchema: z.object({
        pluginId: z
            .string()
            .describe("The plugin identifier for the integration type to set up. Use listAvailableIntegrationTypes to discover valid IDs."),
        displayName: z
            .string()
            .optional()
            .describe("Optional suggested display name for the new integration."),
        fields: z
            .record(z.union([z.string(), z.number(), z.boolean()]))
            .optional()
            .describe("Optional map of non-sensitive field names to values to pre-fill in the form. " +
            "Sensitive fields (passwords, API keys, tokens) are silently ignored. " +
            'Example: {"host": "my-db.example.com", "port": "5432", "database": "mydb"}'),
    }),
    execute: async (input) => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        return clark.context.peer.call.aiOpenIntegrationSetup({
            pluginId: input.pluginId,
            label: input.displayName,
            fields: input.fields,
        });
    },
    getActionName: () => ({
        future: "open integration setup",
        present: "opening integration setup",
        past: "opened integration setup",
    }),
    formatInput: (input) => ({
        targets: [input.pluginId],
        details: input.displayName,
    }),
    getSummary: (input, output) => ({
        input: `pluginId: "${input.pluginId}"`,
        output: output?.success ? "opened" : `failed: ${output?.error}`,
    }),
}));
// ---------------------------------------------------------------------------
// openExistingIntegration
// ---------------------------------------------------------------------------
export const openExistingIntegrationToolFactory = createToolFactory("openExistingIntegration", ({ clark }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Opens an existing integration for editing by its datasource ID. " +
        "Use when the user wants to view or update the configuration of an integration they have already created. " +
        "The integration setup tab will open with the existing field values pre-filled. " +
        "IMPORTANT: If you need to update any fields, you MUST pass them in the `fields` parameter of this call. " +
        "You will not be able to call updateIntegrationFields in the same turn after opening.",
    inputSchema: z.object({
        integrationId: z
            .string()
            .describe("The ID of the existing datasource/integration to open for editing."),
        fields: z
            .record(z.union([z.string(), z.number(), z.boolean()]))
            .optional()
            .describe("Optional map of non-sensitive field names to values to apply immediately after opening. " +
            "Preferred over a separate updateIntegrationFields call when opening and updating together."),
    }),
    execute: async (input) => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        return clark.context.peer.call.aiOpenExistingIntegration({
            integrationId: input.integrationId,
            fields: input.fields,
        });
    },
    getActionName: () => ({
        future: "open existing integration",
        present: "opening existing integration",
        past: "opened existing integration",
    }),
    formatInput: (input) => ({
        targets: [input.integrationId],
    }),
    getSummary: (input, output) => ({
        input: `integrationId: "${input.integrationId}"`,
        output: output?.success
            ? `opened (plugin: ${output?.pluginId})`
            : `failed: ${output?.error}`,
    }),
}));
// ---------------------------------------------------------------------------
// updateIntegrationFields
// ---------------------------------------------------------------------------
export const updateIntegrationFieldsToolFactory = createToolFactory("updateIntegrationFields", ({ clark }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Updates field values in the currently open integration setup form. " +
        "Can only set non-sensitive fields (e.g. host, port, database name, region). " +
        "NEVER attempt to set passwords, API keys, tokens, secrets, or any credential fields — " +
        "those must be entered by the user directly in the form. " +
        "Call this after openIntegrationSetup or openExistingIntegration has opened the form.",
    inputSchema: z.object({
        fields: z
            .record(z.union([z.string(), z.number(), z.boolean()]))
            .describe("Map of field names to values to set in the form. " +
            "Only include non-sensitive configuration fields. " +
            'Example: {"host": "my-db.example.com", "port": "5432", "database": "mydb"}'),
        configId: z
            .string()
            .optional()
            .describe("Optional configuration ID to target. Defaults to the active configuration."),
    }),
    execute: async (input) => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        return clark.context.peer.call.aiUpdateIntegrationFields({
            fields: input.fields,
            configId: input.configId,
        });
    },
    getActionName: () => ({
        future: "update integration fields",
        present: "updating integration fields",
        past: "updated integration fields",
    }),
    formatInput: (input) => ({
        targets: Object.keys(input.fields),
        details: JSON.stringify(input.fields),
    }),
    getSummary: (input, output) => ({
        input: `fields: ${Object.keys(input.fields).join(", ")}`,
        output: output?.success ? "updated" : `failed: ${output?.error}`,
    }),
}));
// ---------------------------------------------------------------------------
// testIntegrationConnection
// ---------------------------------------------------------------------------
export const testIntegrationConnectionToolFactory = createToolFactory("testIntegrationConnection", ({ clark }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Tests the connection for the currently open integration to verify the configuration is correct. " +
        "Use after the user has filled in the required fields to confirm the integration works. " +
        "If the test fails, interpret the error message and suggest which specific fields may need to be corrected. " +
        "Only call this if the formState returned by openIntegrationSetup has isTestable: true. " +
        "Some plugins (e.g. REST API) do not support connection testing — do NOT call this tool for them. " +
        "Only available when the integration setup tab is open.",
    inputSchema: z.object({
        configId: z
            .string()
            .optional()
            .describe("Optional configuration ID to test. Defaults to the active configuration."),
    }),
    execute: async (input) => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        return clark.context.peer.call.aiTestIntegrationConnection({
            configId: input.configId,
        });
    },
    getActionName: () => ({
        future: "test integration connection",
        present: "testing integration connection",
        past: "tested integration connection",
    }),
    getSummary: (_input, output) => ({
        output: output?.success
            ? "connection successful"
            : `failed: ${typeof output?.error === "string" ? output.error : (output?.error?.message ?? "unknown error")}`,
    }),
}));
// ---------------------------------------------------------------------------
// saveIntegration
// ---------------------------------------------------------------------------
export const saveIntegrationToolFactory = createToolFactory("saveIntegration", ({ clark, integrationStore }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Saves the current integration configuration. " +
        "If the formState has isTestable: true, saveIntegration runs connection test first and still saves even if test fails. " +
        "The tool output includes connection test status so you can report what happened. " +
        "If success is false (e.g. validation errors, required fields missing): the form is still open — tell the user what needs fixing and call openExistingIntegration with the current integrationId (or openIntegrationSetup if there is no integrationId yet) so the save card reappears for them to try again. " +
        "After a successful save: if connectionTestPassed is true, call closeIntegrationSetup to close the form; if connectionTestPassed is false and connectionTestAttempted is true, call openExistingIntegration with the returned integrationId to reopen the form so the user can fix the credentials. " +
        "If the plugin is not testable (connectionTestAttempted is false), immediately validate the saved integration: YOU MUST call grepMetadata on the integration first to discover available endpoints, then call executeRequestToIntegration with a GET to a read-only endpoint. Validation ONLY passes if the response status is 2xx and contains actual data. Any non-2xx response (including 405, 403, 500) means validation failed — do not interpret these as proof that credentials are valid. If validation succeeds call closeIntegrationSetup; if it fails call openExistingIntegration with the integrationId so the user can fix the configuration. " +
        "If this tool returns success, treat the integration as saved with the user-provided form values (including sensitive fields entered directly in the form), and do not ask the user to re-enter credentials unless they changed or cleared them. " +
        "Only call this when the user explicitly asks to save, or when the integration setup flow is complete. " +
        "Only available when the integration setup tab is open.",
    inputSchema: z.object({
        force: z
            .boolean()
            .optional()
            .describe("Deprecated: saveIntegration now always saves even when connection test fails."),
    }),
    execute: async (input) => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        const result = await clark.context.peer.call.aiSaveIntegration({
            force: input.force,
        });
        // Register the newly saved integration in the in-memory store so
        // Clark can immediately use it (e.g. via searchIntegrations or
        // executeRequestToIntegration) without waiting for a full refetch.
        if (result.success && result.integrationId && result.pluginId) {
            const savedIntegrationId = result.integrationId;
            const savedPluginId = result.pluginId;
            const savedName = result.integrationName ?? "Untitled";
            integrationStore.registerIntegration({
                id: savedIntegrationId,
                name: savedName,
                type: savedPluginId,
            });
            // Also upsert into FSM context so subsequent turns don't rely solely
            // on promptContext timing to know that the integration was saved.
            if (typeof clark.updateContext === "function") {
                clark.updateContext((context) => {
                    const existing = context.availableIntegrations ?? [];
                    const next = [...existing];
                    const idx = next.findIndex((int) => int.id === savedIntegrationId);
                    const saved = {
                        id: savedIntegrationId,
                        name: savedName,
                        type: savedPluginId,
                        metadata: {},
                    };
                    if (idx >= 0) {
                        next[idx] = saved;
                    }
                    else {
                        next.push(saved);
                    }
                    return {
                        ...context,
                        availableIntegrations: next,
                    };
                });
            }
        }
        return result;
    },
    getActionName: () => ({
        future: "save integration",
        present: "saving integration",
        past: "saved integration",
    }),
    getSummary: (_input, output) => ({
        output: output?.success
            ? `saved (id: ${output?.integrationId})`
            : `failed: ${output?.error}`,
    }),
}));
// ---------------------------------------------------------------------------
// closeIntegrationSetup
// ---------------------------------------------------------------------------
export const closeIntegrationSetupToolFactory = createToolFactory("closeIntegrationSetup", ({ clark }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Closes the integration setup tab. " +
        "Call this after a successful save to clean up the UI before moving on to the next step " +
        "(e.g. building an API that uses the newly created integration).",
    inputSchema: z.object({}),
    execute: async () => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        return clark.context.peer.call.aiCloseIntegrationSetup();
    },
    getActionName: () => ({
        future: "close integration setup",
        present: "closing integration setup",
        past: "closed integration setup",
    }),
    getSummary: () => ({
        output: "closed",
    }),
}));
// ---------------------------------------------------------------------------
// getIntegrationFormSchema (read-only — no UI side effects)
// ---------------------------------------------------------------------------
export const getIntegrationFormSchemaToolFactory = createToolFactory("getIntegrationFormSchema", ({ clark }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    readOnly: true,
    description: "Returns the form schema for a given integration plugin WITHOUT opening the setup UI. " +
        "Use this to discover what fields are required (names, types, which are sensitive) " +
        "so you can ask the user targeted questions before opening the form. " +
        "Requires a pluginId — call listAvailableIntegrationTypes first if unsure.",
    inputSchema: z.object({
        pluginId: z
            .string()
            .describe("The plugin identifier. Use listAvailableIntegrationTypes to discover valid IDs."),
    }),
    execute: async (input) => {
        if (!clark.context.peer) {
            return { success: false, error: "No peer connection available." };
        }
        return clark.context.peer.call.aiGetIntegrationFormSchema({
            pluginId: input.pluginId,
        });
    },
    getActionName: () => ({
        future: "get integration form schema",
        present: "getting integration form schema",
        past: "got integration form schema",
    }),
    formatInput: (input) => ({
        targets: [input.pluginId],
    }),
    getSummary: (input, output) => ({
        input: `pluginId: "${input.pluginId}"`,
        output: output?.success
            ? `${Object.keys(output?.fields ?? {}).length} fields`
            : `failed: ${output?.error}`,
    }),
}));
export const listAvailableIntegrationTypesToolFactory = createToolFactory("listAvailableIntegrationTypes", ({ integrationStore }) => ({
    category: ToolCategory.INTERACTIVE,
    defaultPermission: PermissionLevel.ALLOW,
    description: "Lists all integration types available for setup in Superblocks. " +
        "Use this whenever the user asks what integrations are supported, available, or can be created. " +
        "Also use when the user asks about a specific integration type (e.g. 'do you support Snowflake?'). " +
        "Each entry has an id (the pluginId to pass to openIntegrationSetup) and a human-readable name. " +
        "Prefer this tool over web search for questions about supported integration types.",
    inputSchema: z.object({}),
    readOnly: true,
    execute: async () => {
        return integrationStore.getAvailableIntegrationTypes();
    },
    getActionName: () => ({
        future: "list available integration types",
        present: "listing available integration types",
        past: "listed available integration types",
    }),
    getSummary: (_input, output) => ({
        output: `${Array.isArray(output) ? output.length : 0} types`,
    }),
}));
//# sourceMappingURL=integration-setup.js.map