var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { Resolver, Mutation, Arg, Ctx } from "type-graphql";
import { ActionEngineServer } from "@memberjunction/actions";
import { EntityActionEngineServer } from "@memberjunction/actions";
import { CompositeKey, KeyValuePair, LogError } from "@memberjunction/core";
import { Field, InputType, ObjectType } from "type-graphql";
import { KeyValuePairInput } from "../generic/KeyValuePairInput.js";
import { CopyScalarsAndArrays, UUIDsEqual } from "@memberjunction/global";
import { GetReadOnlyProvider } from "../util.js";
import { ResolverBase } from "../generic/ResolverBase.js";
/**
 * Input type for action parameters
 * Used to pass parameters to actions when invoking them
 */
let ActionParamInput = class ActionParamInput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], ActionParamInput.prototype, "Name", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], ActionParamInput.prototype, "Value", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], ActionParamInput.prototype, "Type", void 0);
ActionParamInput = __decorate([
    InputType()
], ActionParamInput);
export { ActionParamInput };
/**
 * Input type for running an action
 */
let RunActionInput = class RunActionInput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], RunActionInput.prototype, "ActionID", void 0);
__decorate([
    Field(() => [ActionParamInput], { nullable: true }),
    __metadata("design:type", Array)
], RunActionInput.prototype, "Params", void 0);
__decorate([
    Field(() => Boolean, { nullable: true }),
    __metadata("design:type", Boolean)
], RunActionInput.prototype, "SkipActionLog", void 0);
RunActionInput = __decorate([
    InputType()
], RunActionInput);
export { RunActionInput };
/**
 * Represents a collection of key-value pairs that make up a composite key
 * Used for both primary keys and foreign keys
 */
let CompositeKeyInput = class CompositeKeyInput {
};
__decorate([
    Field(() => [KeyValuePairInput]),
    __metadata("design:type", Array)
], CompositeKeyInput.prototype, "KeyValuePairs", void 0);
CompositeKeyInput = __decorate([
    InputType()
], CompositeKeyInput);
export { CompositeKeyInput };
/**
 * Input type for running entity actions
 */
let EntityActionInput = class EntityActionInput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], EntityActionInput.prototype, "EntityActionID", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], EntityActionInput.prototype, "InvocationType", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], EntityActionInput.prototype, "EntityName", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], EntityActionInput.prototype, "EntityID", void 0);
__decorate([
    Field(() => CompositeKeyInput, { nullable: true }),
    __metadata("design:type", CompositeKeyInput)
], EntityActionInput.prototype, "PrimaryKey", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], EntityActionInput.prototype, "ListID", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], EntityActionInput.prototype, "ViewID", void 0);
__decorate([
    Field(() => [ActionParamInput], { nullable: true }),
    __metadata("design:type", Array)
], EntityActionInput.prototype, "Params", void 0);
EntityActionInput = __decorate([
    InputType()
], EntityActionInput);
export { EntityActionInput };
/**
 * Output type for action results
 * Used to return results from actions to clients
 */
let ActionResultOutput = class ActionResultOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ActionResultOutput.prototype, "Success", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], ActionResultOutput.prototype, "Message", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ActionResultOutput.prototype, "ResultCode", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ActionResultOutput.prototype, "ResultData", void 0);
ActionResultOutput = __decorate([
    ObjectType()
], ActionResultOutput);
export { ActionResultOutput };
/**
 * Resolver for action-related GraphQL operations
 * Handles running actions and entity actions through GraphQL
 */
let ActionResolver = class ActionResolver extends ResolverBase {
    /**
     * Mutation for running an action
     * @param input The input parameters for running the action
     * @param ctx The GraphQL context containing user authentication information
     * @returns The result of running the action
     */
    async RunAction(input, ctx) {
        try {
            // Check API key scope authorization for action execution
            await this.CheckAPIKeyScopeAuthorization('action:execute', input.ActionID, ctx.userPayload);
            // Get the user from context
            const user = ctx.userPayload.userRecord;
            if (!user) {
                throw new Error("User is not authenticated");
            }
            // Initialize the action engine
            await ActionEngineServer.Instance.Config(false, user);
            // Get the action by ID
            const action = this.findActionById(input.ActionID);
            // Parse the parameters
            const params = this.parseActionParameters(input.Params);
            // Run the action
            const result = await this.executeAction(action, user, params, input.SkipActionLog);
            // Return the result
            return this.createActionResult(result);
        }
        catch (e) {
            return this.handleActionError(e);
        }
    }
    /**
     * Finds an action by its ID
     * @param actionID The ID of the action to find
     * @returns The action
     * @throws Error if the action is not found
     * @private
     */
    findActionById(actionID) {
        const action = ActionEngineServer.Instance.Actions.find(a => UUIDsEqual(a.ID, actionID));
        if (!action) {
            throw new Error(`Action with ID ${actionID} not found`);
        }
        return action;
    }
    /**
     * Parses action parameters from the input
     * @param inputParams The input parameters
     * @returns The parsed parameters
     * @private
     */
    parseActionParameters(inputParams) {
        if (!inputParams || inputParams.length === 0) {
            return [];
        }
        return inputParams.map(p => {
            let value = p.Value;
            // Try to parse JSON for complex values
            try {
                if (p.Value && (p.Type === 'object' || p.Type === 'array')) {
                    value = JSON.parse(p.Value);
                }
            }
            catch (e) {
                // If parsing fails, keep the original value
                const error = e;
                LogError(`Failed to parse parameter value as JSON: ${error.message}`);
            }
            return {
                Name: p.Name,
                Value: value,
                Type: 'Input' // Default to Input type since we're sending parameters
            };
        });
    }
    /**
     * Executes an action
     * @param action The action to execute
     * @param user The user context
     * @param params The action parameters
     * @param skipActionLog Whether to skip action logging
     * @returns The action result
     * @private
     */
    async executeAction(action, user, params, skipActionLog) {
        return await ActionEngineServer.Instance.RunAction({
            Action: action,
            ContextUser: user,
            Params: params,
            SkipActionLog: skipActionLog,
            Filters: []
        });
    }
    /**
     * Creates an action result from the execution result
     * @param result The execution result
     * @returns The formatted action result
     * @private
     */
    createActionResult(result) {
        const x = (result.Params || result.RunParams.Params || [])
            .filter(p => p.Type.trim().toLowerCase() === 'output' ||
            p.Type.trim().toLowerCase() === 'both');
        return {
            Success: result.Success,
            Message: result.Message,
            ResultCode: result.Result?.ResultCode,
            ResultData: x && x.length > 0 ? JSON.stringify(CopyScalarsAndArrays(x)) : undefined
        };
    }
    /**
     * Handles errors in the action resolver
     * @param e The error
     * @returns An error result
     * @private
     */
    handleActionError(e) {
        const error = e;
        LogError(`Error in RunAction resolver: ${error}`);
        return {
            Success: false,
            Message: `Error executing action: ${error.message}`
        };
    }
    /**
     * Mutation for running an entity action
     * @param input The input parameters for running the entity action
     * @param ctx The GraphQL context containing user authentication information
     * @returns The result of running the entity action
     */
    async RunEntityAction(input, ctx) {
        try {
            // Check API key scope authorization for entity action execution
            await this.CheckAPIKeyScopeAuthorization('action:execute', input.EntityActionID, ctx.userPayload);
            const user = ctx.userPayload.userRecord;
            if (!user) {
                throw new Error("User is not authenticated");
            }
            // Initialize the entity action engine
            await EntityActionEngineServer.Instance.Config(false, user);
            // Get the entity action by ID
            const entityAction = this.getEntityAction(input.EntityActionID);
            // Create the base parameters
            const params = this.createBaseParams(entityAction, input.InvocationType, user);
            // Add entity object if we have entity information and primary key
            if ((input.EntityID || input.EntityName) && input.PrimaryKey && input.PrimaryKey.KeyValuePairs.length > 0) {
                await this.addEntityObject(ctx.providers, params, input, user);
            }
            // Add other parameters
            this.addOptionalParams(params, input);
            // Run the entity action
            const result = await EntityActionEngineServer.Instance.RunEntityAction(params);
            // Return the result
            return {
                Success: result.Success,
                Message: result.Message,
                ResultData: JSON.stringify(result)
            };
        }
        catch (e) {
            return this.handleError(e);
        }
    }
    /**
     * Gets an entity action by ID
     * @param actionID The ID of the entity action
     * @returns The entity action
     * @throws Error if entity action is not found
     * @private
     */
    getEntityAction(actionID) {
        const entityAction = EntityActionEngineServer.Instance.EntityActions.find(ea => UUIDsEqual(ea.ID, actionID));
        if (!entityAction) {
            throw new Error(`EntityAction with ID ${actionID} not found`);
        }
        return entityAction;
    }
    /**
     * Creates the base parameters for the entity action
     * @param entityAction The entity action
     * @param invocationTypeName The invocation type name
     * @param user The authenticated user
     * @returns The base parameters
     * @private
     */
    createBaseParams(entityAction, invocationTypeName, user) {
        return {
            EntityAction: entityAction,
            InvocationType: { Name: invocationTypeName },
            ContextUser: user,
            Params: [],
        };
    }
    /**
     * Adds the entity object to the parameters
     * @param params The parameters to add to
     * @param input The input parameters
     * @param user The authenticated user
     * @private
     */
    async addEntityObject(providers, params, input, user) {
        const md = GetReadOnlyProvider(providers);
        // Find the entity by ID or name
        let entity;
        if (input.EntityName) {
            entity = md.Entities.find(e => e.Name === input.EntityName);
            if (!entity) {
                throw new Error(`Entity with name ${input.EntityName} not found`);
            }
        }
        else if (input.EntityID) {
            entity = md.Entities.find(e => UUIDsEqual(e.ID, input.EntityID));
            if (!entity) {
                throw new Error(`Entity with ID ${input.EntityID} not found`);
            }
        }
        if (!entity) {
            throw new Error("Entity information is required");
        }
        // Create a composite key and load the entity object
        const compositeKey = this.createCompositeKey(entity, input.PrimaryKey);
        const entityObject = await md.GetEntityObject(entity.Name);
        await entityObject.InnerLoad(compositeKey);
        params['EntityObject'] = entityObject;
    }
    /**
     * Creates a composite key from the input
     * @param entity The entity information
     * @param primaryKey The primary key input
     * @returns The composite key
     * @private
     */
    createCompositeKey(entity, primaryKey) {
        const compositeKey = new CompositeKey();
        for (const kvp of primaryKey.KeyValuePairs) {
            // Convert value based on the field type if necessary
            const field = entity.Fields.find(f => f.Name === kvp.Key);
            let value = kvp.Value;
            // If the field is found, try to convert to proper type
            if (field) {
                value = this.convertValueToProperType(value, field);
            }
            // Add to composite key
            const kvPair = new KeyValuePair();
            kvPair.FieldName = kvp.Key;
            kvPair.Value = value;
            compositeKey.KeyValuePairs.push(kvPair);
        }
        return compositeKey;
    }
    /**
     * Converts a value to the proper type based on the field information
     * @param value The value to convert
     * @param field The field information
     * @returns The converted value
     * @private
     */
    convertValueToProperType(value, field) {
        // Simple conversion, could be enhanced for other types
        if (field.Type.toLowerCase().match(/int|decimal|float|money|numeric|real/) && !isNaN(Number(value))) {
            return Number(value);
        }
        else if (field.Type.toLowerCase().includes('date') && !isNaN(Date.parse(value))) {
            return new Date(value);
        }
        return value;
    }
    /**
     * Adds optional parameters to the entity action parameters
     * @param params The parameters to add to
     * @param input The input parameters
     * @private
     */
    addOptionalParams(params, input) {
        // Add list ID if provided
        if (input.ListID) {
            params['ListID'] = input.ListID;
        }
        // Add view ID if provided
        if (input.ViewID) {
            params['ViewID'] = input.ViewID;
        }
        // Add additional parameters if provided
        if (input.Params && input.Params.length > 0) {
            params.Params = input.Params.map(p => this.convertParameterValue(p));
        }
    }
    /**
     * Converts a parameter value to the proper format
     * @param p The parameter to convert
     * @returns The converted parameter
     * @private
     */
    convertParameterValue(p) {
        let value = p.Value;
        // Try to parse JSON for complex values
        try {
            if (p.Value && (p.Type === 'object' || p.Type === 'array')) {
                value = JSON.parse(p.Value);
            }
        }
        catch (e) {
            // If parsing fails, keep the original value
            const error = e;
            LogError(`Failed to parse parameter value as JSON: ${error.message}`);
        }
        return {
            Name: p.Name,
            Value: value,
            Type: 'Input' // Default to Input type since we're sending parameters
        };
    }
    /**
     * Handles errors in the entity action resolver
     * @param e The error
     * @returns An error result
     * @private
     */
    handleError(e) {
        const error = e;
        LogError(`Error in RunEntityAction resolver: ${error}`);
        return {
            Success: false,
            Message: `Error executing entity action: ${error.message}`
        };
    }
};
__decorate([
    Mutation(() => ActionResultOutput),
    __param(0, Arg("input")),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [RunActionInput, Object]),
    __metadata("design:returntype", Promise)
], ActionResolver.prototype, "RunAction", null);
__decorate([
    Mutation(() => ActionResultOutput),
    __param(0, Arg("input")),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [EntityActionInput, Object]),
    __metadata("design:returntype", Promise)
], ActionResolver.prototype, "RunEntityAction", null);
ActionResolver = __decorate([
    Resolver()
], ActionResolver);
export { ActionResolver };
//# sourceMappingURL=ActionResolver.js.map