import { tool } from 'ai';
import { resolveLocalWorkflowDiagram } from '../tools/present-workflow-result.js';
import { resolveN8nRuntimeState, YagrN8nConfigService } from '../config/n8n-config-service.js';
const DEFAULT_POST_SYNC_ALLOWED_TOOL_NAMES = [
    'presentWorkflowResult',
    'reportProgress',
    'requestRequiredAction',
];
function createFallbackRuntimeStrategy() {
    return {
        capabilityProfile: {
            provider: 'openai',
            model: 'fallback',
            toolCalling: 'native',
            supportsParallelToolCalls: true,
            supportsStructuredOutputs: true,
            supportsStreamingToolCalls: true,
            supportsForcedToolChoice: true,
            prefersStrictToolSchemas: false,
        },
        tooling: {
            allowedToolNamesAfterWorkflowSync: DEFAULT_POST_SYNC_ALLOWED_TOOL_NAMES,
            availableToolNames: [],
            toolCallMode: 'parallel',
            executionCriticalToolNames: [],
        },
        executionMode: 'stream',
        toolCallStreaming: true,
        inspectMaxSteps: 0,
        executeMaxSteps: 0,
        recoveryMaxSteps: 0,
        inspectDirectives: [],
        executeDirectives: [],
        recoveryDirectives: [],
    };
}
function buildWorkflowPresentationRequiredAction(workflowId) {
    return {
        id: `pull-workflow-${workflowId}`,
        kind: 'external',
        title: 'Pull workflow before presenting it',
        message: `Pull workflow ${workflowId} before calling presentWorkflowResult so the card uses the canonical local workflow-map.`,
        detail: 'This workflow is not currently available as a local .workflow.ts file in the active Yagr workspace or Yagr home. Run n8nac pull for the workflow ID first, then present it.',
        resumable: true,
    };
}
export function createWorkflowPresentationGuardHook() {
    return {
        beforeTool: async ({ toolName, args }) => {
            if (toolName !== 'presentWorkflowResult' || !args || typeof args !== 'object') {
                return;
            }
            const workflowId = typeof args.workflowId === 'string'
                ? args.workflowId
                : undefined;
            const providedDiagram = typeof args.diagram === 'string'
                ? args.diagram.trim()
                : '';
            if (providedDiagram) {
                return;
            }
            if (!workflowId || resolveLocalWorkflowDiagram(workflowId)) {
                return;
            }
            return {
                allowed: false,
                message: `Workflow ${workflowId} must be pulled locally before it can be presented.`,
                requiredAction: buildWorkflowPresentationRequiredAction(workflowId),
            };
        },
    };
}
function asRecord(value) {
    return value && typeof value === 'object' ? value : undefined;
}
function asString(value) {
    return typeof value === 'string' && value.length > 0 ? value : undefined;
}
function asNumber(value) {
    return typeof value === 'number' ? value : undefined;
}
export function createWorkflowSyncCompletionGuardHook(strategy) {
    let workflowSyncSettled = false;
    const toolsAllowedAfterSuccessfulSync = new Set(strategy.tooling.allowedToolNamesAfterWorkflowSync);
    return {
        beforeTool: async ({ toolName }) => {
            if (!workflowSyncSettled) {
                return;
            }
            if (toolsAllowedAfterSuccessfulSync.has(toolName)) {
                return;
            }
            return {
                allowed: false,
                message: 'Workflow already pushed and verified. Stop using tools now and return the final user-facing response.',
            };
        },
        afterTool: async ({ toolName, args, result }) => {
            if (toolName !== 'n8nac') {
                return;
            }
            const normalizedArgs = asRecord(args);
            const normalizedResult = asRecord(result);
            const action = asString(normalizedArgs?.action);
            const exitCode = asNumber(normalizedResult?.exitCode);
            if (exitCode !== 0) {
                return;
            }
            if (action === 'push' || action === 'verify') {
                workflowSyncSettled = true;
            }
        },
    };
}
function isConfiguredWorkspaceAvailable(configService = new YagrN8nConfigService()) {
    return resolveN8nRuntimeState(configService, process.env, {
        allowEnvironmentFallback: process.env.YAGR_ALLOW_N8N_ENV === '1',
    }).initialized;
}
export function createN8nSetupGuardHook() {
    let setupCheckKnown = false;
    let workspaceInitialized = isConfiguredWorkspaceAvailable();
    let credentialsAvailable = resolveN8nRuntimeState(new YagrN8nConfigService(), process.env, {
        allowEnvironmentFallback: process.env.YAGR_ALLOW_N8N_ENV === '1',
    }).credentialsAvailable;
    return {
        beforeTool: async ({ toolName, args }) => {
            if (toolName !== 'n8nac') {
                return;
            }
            const normalizedArgs = asRecord(args);
            const action = asString(normalizedArgs?.action);
            if (action !== 'init_auth' && action !== 'init_project') {
                return;
            }
            if (action === 'init_auth' && credentialsAvailable) {
                return {
                    allowed: false,
                    message: 'n8n credentials are already available. Do not rerun init_auth. Continue with setup_check or init_project.',
                };
            }
            if (workspaceInitialized) {
                return {
                    allowed: false,
                    message: 'The n8n workspace is already initialized. Do not rerun init_auth or init_project. Continue directly with workflow file creation, validate, push, and verify.',
                };
            }
            if (!setupCheckKnown) {
                return {
                    allowed: false,
                    message: 'Do not run init_auth or init_project speculatively. Call n8nac setup_check first, and only continue with setup if it reports that the workspace is not initialized.',
                };
            }
        },
        afterTool: async ({ toolName, args, result }) => {
            if (toolName !== 'n8nac') {
                return;
            }
            const normalizedArgs = asRecord(args);
            const action = asString(normalizedArgs?.action);
            if (action !== 'setup_check') {
                return;
            }
            const normalizedResult = asRecord(result);
            setupCheckKnown = true;
            workspaceInitialized = normalizedResult?.initialized === true;
            credentialsAvailable = normalizedResult?.credentialsAvailable === true || credentialsAvailable;
        },
    };
}
export function createDefaultRuntimeHooks() {
    return createDefaultRuntimeHooksForStrategy(createFallbackRuntimeStrategy());
}
export function createDefaultRuntimeHooksForStrategy(strategy) {
    return [
        createN8nSetupGuardHook(),
        createWorkflowPresentationGuardHook(),
        createWorkflowSyncCompletionGuardHook(strategy),
    ];
}
export function wrapToolsWithRuntimeHooks(tools, hooks, getContext, satisfiedRequiredActionIds = []) {
    if (!hooks || hooks.length === 0) {
        return tools;
    }
    const approvedActionIds = new Set(satisfiedRequiredActionIds);
    const wrappedEntries = Object.entries(tools).map(([toolName, originalTool]) => {
        if (typeof originalTool.execute !== 'function') {
            return [toolName, originalTool];
        }
        const wrappedTool = tool({
            description: originalTool.description,
            parameters: originalTool.parameters,
            experimental_toToolResultContent: originalTool.experimental_toToolResultContent,
            execute: async (args, toolOptions) => {
                const runtimeContext = {
                    runId: getContext().runId,
                    phase: getContext().phase ?? undefined,
                    state: getContext().state,
                };
                const hookContext = {
                    ...runtimeContext,
                    toolName,
                    args,
                };
                for (const hook of hooks) {
                    const decision = await hook.beforeTool?.(hookContext);
                    if (decision && decision.allowed === false) {
                        if (decision.requiredAction && approvedActionIds.has(decision.requiredAction.id)) {
                            continue;
                        }
                        return {
                            ok: false,
                            blocked: true,
                            error: decision.message ?? `Tool ${toolName} blocked by runtime policy.`,
                            requiredAction: decision.requiredAction,
                        };
                    }
                }
                const result = await originalTool.execute?.(args, toolOptions);
                for (const hook of hooks) {
                    await hook.afterTool?.({
                        ...hookContext,
                        result,
                    });
                }
                return result;
            },
        });
        return [toolName, wrappedTool];
    });
    return Object.fromEntries(wrappedEntries);
}
//# sourceMappingURL=policy-hooks.js.map