import get from 'lodash/get.js';
import { AGENTS_SCHEMA_PATH, getAgentMap, getGuardConfig, isValidAgentMutation } from '../agents.js';
import { createGetNamespace, getToolRef, parsePropertyRef, propertyRefItemToPath } from "../refs.js";
import { isBuiltinMutation, isBuiltinQuery } from "../schema-util.js";
import { validateExpression } from "../util/expressions.js";
import { findDuplicateIndices, pushErrors } from "./util.js";
export function validateAIToolConfig(projectSchema, getNamespace, tool, basePath) {
    const toolRef = getToolRef(tool);
    const path = typeof tool === 'string' ? basePath : basePath.concat('ref');
    const parsed = parsePropertyRef(toolRef);
    if (!parsed) {
        return {
            type: 'conflict',
            path,
            message: `Unable to parse property ref "${toolRef}"`
        };
    }
    if (parsed.layerId !== 'local') {
        return;
    }
    if ((parsed.shapeName === 'Query' && isBuiltinQuery(parsed.propertyName)) ||
        (parsed.shapeName === 'Mutation' && isBuiltinMutation(parsed.propertyName))) {
        return;
    }
    const propertyPath = propertyRefItemToPath(getNamespace, parsed);
    if (propertyPath[0] !== 'queries' && propertyPath[0] !== 'mutations') {
        return {
            type: 'conflict',
            path,
            message: 'Tools must be a query or mutation'
        };
    }
    const property = get(projectSchema, propertyPath);
    if (propertyPath[0] === 'mutations' && isValidAgentMutation(projectSchema, propertyPath[1])) {
        return;
    }
    if (!property) {
        return {
            type: 'notFound',
            path,
            message: `Missing tool ${propertyPath[0] === 'queries' ? 'query' : 'mutation'} "${toolRef}"`
        };
    }
}
function validateAgentTransitions(parentPath, inputNames, transitions) {
    const errors = [];
    transitions.forEach((transition, transitionIndex) => {
        if (transition.type === 'suspend') {
            if (!inputNames.has(transition.input)) {
                errors.push({
                    path: [...parentPath, transitionIndex, 'input'],
                    type: 'notFound',
                    message: `Invalid input "${transition.input}"`
                });
            }
        }
        if (transition.condition) {
            const conditionValidation = validateExpression(transition.condition);
            if (!conditionValidation.valid) {
                const common = {
                    path: [...parentPath, transitionIndex, 'expression'],
                    type: 'conflict'
                };
                errors.push(...conditionValidation.errors.map((message) => ({ ...common, message })));
            }
        }
    });
    return errors;
}
export function validateAgents(projectSchema) {
    const getNamespace = createGetNamespace(projectSchema);
    const errors = [];
    const agents = getAgentMap(projectSchema);
    if (!agents) {
        return errors;
    }
    for (const [agentName, agent] of Object.entries(agents)) {
        const stateNames = new Set();
        const agentPath = [...AGENTS_SCHEMA_PATH, agentName];
        const inputNames = new Set(agent.api.inputs.map((input) => input.name));
        for (const [name, indices] of findDuplicateIndices(agent.api.inputs, 'name')) {
            for (const index of indices) {
                errors.push({
                    path: [...agentPath, 'api', 'inputs', index, 'name'],
                    type: 'conflict',
                    message: `Duplicate input name "${name}"`
                });
            }
        }
        pushErrors(errors, validateAgentTransitions([...agentPath, 'start', 'transitions'], inputNames, agent.start.transitions));
        for (const [stateId, state] of Object.entries(agent.states)) {
            const statePath = [...agentPath, 'states', stateId];
            if (stateNames.has(state.name)) {
                errors.push({
                    path: [...statePath, 'name'],
                    type: 'conflict',
                    message: `Duplicate state name "${state.name}"`
                });
            }
            else {
                stateNames.add(state.name);
            }
            if (state.variables) {
                state.variables.forEach((variable, variableIndex) => {
                    variable.steps.forEach((step, stepIndex) => {
                        const stepPath = [...statePath, 'variables', variableIndex, 'steps', stepIndex];
                        if (step.condition) {
                            const conditionValidation = validateExpression(step.condition);
                            if (!conditionValidation.valid) {
                                const common = {
                                    path: [...stepPath, 'condition'],
                                    type: 'conflict'
                                };
                                errors.push(...conditionValidation.errors.map((message) => ({ ...common, message })));
                            }
                        }
                        const exprValidation = validateExpression(step.expression);
                        if (!exprValidation.valid) {
                            const common = {
                                path: [...stepPath, 'expression'],
                                type: 'conflict'
                            };
                            errors.push(...exprValidation.errors.map((message) => ({ ...common, message })));
                        }
                    });
                });
            }
            if (state.transitions) {
                errors.push(...validateAgentTransitions([...statePath, 'transitions'], inputNames, state.transitions));
            }
            if (state.execution.type === 'generate' || state.execution.type === 'chat') {
                if (!state.execution.tools) {
                    continue;
                }
                for (const [name, indices] of findDuplicateIndices(state.execution.tools, 'name')) {
                    for (const index of indices) {
                        errors.push({
                            path: [...statePath, 'execution', 'tools', index, 'name'],
                            type: 'conflict',
                            message: `Duplicate tool name "${name}"`
                        });
                    }
                }
                state.execution.tools.forEach((tool, toolIndex) => {
                    pushErrors(errors, validateAIToolConfig(projectSchema, getNamespace, tool, [
                        ...statePath,
                        'execution',
                        'tools',
                        toolIndex,
                        'ref'
                    ]));
                });
            }
        }
        if (agent.guards) {
            agent.guards.forEach(({ guardId }, index) => {
                if (!getGuardConfig(projectSchema, guardId)) {
                    errors.push({
                        path: [...agentPath, 'guards', index, 'guardId'],
                        type: 'notFound',
                        message: `Invalid guardId "${guardId}"`
                    });
                }
            });
        }
        if (agent.historyStrategies) {
            const seenStrategyNames = new Set();
            for (const [historyStrategyId, historyStrategy] of Object.entries(agent.historyStrategies)) {
                if (seenStrategyNames.has(historyStrategy.name)) {
                    errors.push({
                        path: [...agentPath, 'historyStrategies', historyStrategyId, 'name'],
                        type: 'conflict',
                        message: `Duplicate history strategy name "${historyStrategy.name}"`
                    });
                }
                else {
                    seenStrategyNames.add(historyStrategy.name);
                }
            }
        }
    }
    return errors;
}
