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 { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType } from 'type-graphql';
import { CompositeKey, LogError, Metadata, RunView } from '@memberjunction/core';
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
import { CompositeKeyInputType, CompositeKeyOutputType } from '../generic/KeyInputOutputTypes.js';
export var SyncDataActionType;
(function (SyncDataActionType) {
    SyncDataActionType["Create"] = "Create";
    SyncDataActionType["Update"] = "Update";
    SyncDataActionType["CreateOrUpdate"] = "CreateOrUpdate";
    SyncDataActionType["Delete"] = "Delete";
    SyncDataActionType["DeleteWithFilter"] = "DeleteWithFilter";
})(SyncDataActionType || (SyncDataActionType = {}));
registerEnumType(SyncDataActionType, {
    name: "SyncDataActionType",
    description: "Specifies the type of action to be taken in syncing, Create, Update, CreateOrUpdate, Delete"
});
let ActionItemInputType = class ActionItemInputType {
    EntityName;
    PrimaryKey;
    AlternateKey;
    Type;
    RecordJSON;
    DeleteFilter;
};
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], ActionItemInputType.prototype, "EntityName", void 0);
__decorate([
    Field(() => CompositeKeyInputType, { nullable: true }),
    __metadata("design:type", CompositeKeyInputType)
], ActionItemInputType.prototype, "PrimaryKey", void 0);
__decorate([
    Field(() => CompositeKeyInputType, { nullable: true }),
    __metadata("design:type", CompositeKeyInputType)
], ActionItemInputType.prototype, "AlternateKey", void 0);
__decorate([
    Field(() => SyncDataActionType),
    __metadata("design:type", String)
], ActionItemInputType.prototype, "Type", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ActionItemInputType.prototype, "RecordJSON", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ActionItemInputType.prototype, "DeleteFilter", void 0);
ActionItemInputType = __decorate([
    InputType()
], ActionItemInputType);
export { ActionItemInputType };
let ActionItemOutputType = class ActionItemOutputType {
    Success;
    ErrorMessage;
    EntityName;
    PrimaryKey;
    AlternateKey;
    Type;
    RecordJSON;
    DeleteFilter;
};
__decorate([
    Field(() => Boolean),
    __metadata("design:type", Boolean)
], ActionItemOutputType.prototype, "Success", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], ActionItemOutputType.prototype, "ErrorMessage", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], ActionItemOutputType.prototype, "EntityName", void 0);
__decorate([
    Field(() => CompositeKeyOutputType, { nullable: true }),
    __metadata("design:type", CompositeKeyOutputType)
], ActionItemOutputType.prototype, "PrimaryKey", void 0);
__decorate([
    Field(() => CompositeKeyOutputType, { nullable: true }),
    __metadata("design:type", CompositeKeyOutputType)
], ActionItemOutputType.prototype, "AlternateKey", void 0);
__decorate([
    Field(() => SyncDataActionType),
    __metadata("design:type", String)
], ActionItemOutputType.prototype, "Type", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ActionItemOutputType.prototype, "RecordJSON", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ActionItemOutputType.prototype, "DeleteFilter", void 0);
ActionItemOutputType = __decorate([
    ObjectType()
], ActionItemOutputType);
export { ActionItemOutputType };
let SyncDataResultType = class SyncDataResultType {
    Success;
    Results = [];
};
__decorate([
    Field(() => Boolean),
    __metadata("design:type", Boolean)
], SyncDataResultType.prototype, "Success", void 0);
__decorate([
    Field(() => [ActionItemOutputType]),
    __metadata("design:type", Array)
], SyncDataResultType.prototype, "Results", void 0);
SyncDataResultType = __decorate([
    ObjectType()
], SyncDataResultType);
export { SyncDataResultType };
const __metadata_DatasetItems = [];
export class SyncDataResolver {
    async SyncData(items, context) {
        try {
            const md = new Metadata();
            const results = [];
            for (const item of items) {
                results.push(await this.SyncSingleItem(item, context, md, context.userPayload));
            }
            if (await this.DoSyncItemsAffectMetadata(context.userPayload.userRecord, items)) {
                await md.Refresh();
            }
            const overallSuccess = !results.some((r) => !r.Success);
            return { Success: overallSuccess, Results: results };
        }
        catch (err) {
            LogError(err);
            throw new Error('SyncDataResolver::SyncData --- Error Syncing Data\n\n' + err);
        }
    }
    async GetLowercaseMetadataEntitiesList(user, forceRefresh = false) {
        if (forceRefresh || __metadata_DatasetItems.length === 0) {
            const rv = new RunView();
            const result = await rv.RunView({
                EntityName: "Dataset Items",
                ExtraFilter: "Dataset = 'MJ_Metadata'",
            }, user);
            if (result && result.Success) {
                __metadata_DatasetItems.length = 0;
                __metadata_DatasetItems.push(...result.Results.map((r) => {
                    return r.Entity.trim().toLowerCase();
                }));
            }
        }
        return __metadata_DatasetItems;
    }
    async DoSyncItemsAffectMetadata(user, items) {
        const entitiesToCheck = await this.GetLowercaseMetadataEntitiesList(user, false);
        for (const item of items) {
            if (entitiesToCheck.find(e => e === item.EntityName.trim().toLowerCase())) {
                return true;
            }
        }
        return false;
    }
    async SyncSingleItem(item, context, md, userPayload) {
        const result = new ActionItemOutputType();
        result.AlternateKey = item.AlternateKey;
        result.PrimaryKey = item.PrimaryKey;
        result.DeleteFilter = item.DeleteFilter;
        result.EntityName = item.EntityName;
        result.RecordJSON = item.RecordJSON;
        result.Type = item.Type;
        result.Success = false;
        result.ErrorMessage = '';
        try {
            const e = md.Entities.find((e) => e.Name === item.EntityName);
            if (e) {
                const pk = item.PrimaryKey ? new CompositeKey(item.PrimaryKey.KeyValuePairs) : null;
                const ak = item.AlternateKey ? new CompositeKey(item.AlternateKey.KeyValuePairs) : null;
                const entityObject = item.Type === SyncDataActionType.DeleteWithFilter ? null : await md.GetEntityObject(e.Name, context.userPayload.userRecord);
                const fieldValues = item.RecordJSON ? JSON.parse(item.RecordJSON) : {};
                switch (item.Type) {
                    case SyncDataActionType.Create:
                        await this.SyncSingleItemCreate(entityObject, fieldValues, result, userPayload);
                        break;
                    case SyncDataActionType.Update:
                        await this.SyncSingleItemUpdate(entityObject, pk, ak, fieldValues, result, userPayload);
                        break;
                    case SyncDataActionType.CreateOrUpdate:
                        await this.SyncSingleItemCreateOrUpdate(entityObject, pk, ak, fieldValues, result, userPayload);
                        break;
                    case SyncDataActionType.Delete:
                        await this.SyncSingleItemDelete(entityObject, pk, ak, result, userPayload);
                        break;
                    case SyncDataActionType.DeleteWithFilter:
                        await this.SyncSingleItemDeleteWithFilter(item.EntityName, item.DeleteFilter, result, context.userPayload.userRecord, userPayload);
                        break;
                    default:
                        throw new Error('Invalid SyncDataActionType');
                }
            }
            else {
                throw new Error('Entity not found');
            }
        }
        catch (err) {
            result.ErrorMessage = typeof err === 'string' ? err : err.message;
            LogError(err);
        }
        finally {
            return result;
        }
    }
    async SyncSingleItemDeleteWithFilter(entityName, filter, result, user, userPayload) {
        try {
            let overallSuccess = true;
            let combinedErrorMessage = "";
            const rv = new RunView();
            const data = await rv.RunView({
                EntityName: entityName,
                ExtraFilter: filter,
                ResultType: 'entity_object'
            }, user);
            if (data && data.Success) {
                for (const entityObject of data.Results) {
                    if (!await entityObject.Delete()) {
                        overallSuccess = false;
                        combinedErrorMessage += 'Failed to delete the item :' + entityObject.LatestResult.CompleteMessage + '\n';
                    }
                }
                result.Success = overallSuccess;
                if (!overallSuccess) {
                    result.ErrorMessage = combinedErrorMessage;
                }
            }
            else {
                result.Success = false;
                result.ErrorMessage = 'Failed to run the view to get the list of items to delete for entity: ' + entityName + ' with filter: ' + filter + '\n';
            }
        }
        catch (e) {
            result.ErrorMessage = typeof e === 'string' ? e : e.message;
        }
    }
    async LoadFromAlternateKey(entityName, alternateKey, user) {
        try {
            const rv = new RunView();
            const md = new Metadata();
            const entity = md.EntityByName(entityName);
            const r = await rv.RunView({
                EntityName: entityName,
                ExtraFilter: alternateKey.KeyValuePairs.map((kvp) => {
                    const fieldInfo = entity.Fields.find((f) => f.Name === kvp.FieldName);
                    const quotes = fieldInfo.NeedsQuotes ? "'" : '';
                    return `${kvp.FieldName} = ${quotes}${kvp.Value}${quotes}`;
                }).join(' AND '),
                ResultType: 'entity_object'
            }, user);
            if (r && r.Success && r.Results.length === 1) {
                return r.Results[0];
            }
            else {
                return null;
            }
        }
        catch (e) {
            LogError(e);
            return null;
        }
    }
    async SyncSingleItemCreateOrUpdate(entityObject, pk, ak, fieldValues, result, userPayload) {
        if (!pk || pk.KeyValuePairs.length === 0) {
            const altKeyResult = await this.LoadFromAlternateKey(entityObject.EntityInfo.Name, ak, entityObject.ContextCurrentUser);
            if (!altKeyResult) {
                await this.SyncSingleItemCreate(entityObject, fieldValues, result, userPayload);
            }
            else {
                await this.InnerSyncSingleItemUpdate(altKeyResult, fieldValues, result, userPayload);
            }
        }
        else {
            if (await entityObject.InnerLoad(pk)) {
                await this.InnerSyncSingleItemUpdate(entityObject, fieldValues, result, userPayload);
            }
            else {
                await this.SyncSingleItemCreate(entityObject, fieldValues, result, userPayload);
            }
        }
    }
    async SyncSingleItemDelete(entityObject, pk, ak, result, userPayload) {
        if (!pk || pk.KeyValuePairs.length === 0) {
            const altKeyResult = await this.LoadFromAlternateKey(entityObject.EntityInfo.Name, ak, entityObject.ContextCurrentUser);
            if (!altKeyResult) {
                result.ErrorMessage = 'Failed to load the item, it is possible the record with the specified primary key does not exist';
            }
            else {
                result.RecordJSON = await altKeyResult.GetDataObjectJSON({
                    includeRelatedEntityData: false,
                    excludeFields: [],
                    omitEmptyStrings: false,
                    relatedEntityList: [],
                    omitNullValues: false,
                    oldValues: false
                });
                if (await altKeyResult.Delete()) {
                    result.Success = true;
                }
                else {
                    result.ErrorMessage = 'Failed to delete the item :' + entityObject.LatestResult.CompleteMessage;
                }
            }
        }
        else if (await entityObject.InnerLoad(pk)) {
            result.RecordJSON = await entityObject.GetDataObjectJSON({
                includeRelatedEntityData: false,
                excludeFields: [],
                omitEmptyStrings: false,
                relatedEntityList: [],
                omitNullValues: false,
                oldValues: false
            });
            if (await entityObject.Delete()) {
                result.Success = true;
            }
            else {
                result.ErrorMessage = 'Failed to delete the item :' + entityObject.LatestResult.CompleteMessage;
            }
        }
        else {
            result.ErrorMessage = 'Failed to load the item, it is possible the record with the specified primary key does not exist';
        }
    }
    async SyncSingleItemCreate(entityObject, fieldValues, result, userPayload) {
        const noPKValues = { ...fieldValues };
        entityObject.EntityInfo.PrimaryKeys.forEach((pk) => {
            delete noPKValues[pk.Name];
        });
        entityObject.SetMany(noPKValues);
        if (await entityObject.Save()) {
            result.Success = true;
            result.PrimaryKey = new CompositeKey(entityObject.PrimaryKeys.map((pk) => ({ FieldName: pk.Name, Value: pk.Value })));
            result.RecordJSON = await entityObject.GetDataObjectJSON({
                includeRelatedEntityData: false,
                excludeFields: [],
                omitEmptyStrings: false,
                relatedEntityList: [],
                omitNullValues: false,
                oldValues: false
            });
        }
        else {
            result.ErrorMessage = 'Failed to create the item :' + entityObject.LatestResult.CompleteMessage;
        }
    }
    async SyncSingleItemUpdate(entityObject, pk, ak, fieldValues, result, userPayload) {
        if (!pk || pk.KeyValuePairs.length === 0) {
            const altKeyResult = await this.LoadFromAlternateKey(entityObject.EntityInfo.Name, ak, entityObject.ContextCurrentUser);
            if (!altKeyResult) {
                result.ErrorMessage = 'Failed to load the item, it is possible the record with the specified alternate key does not exist';
            }
            else {
                await this.InnerSyncSingleItemUpdate(altKeyResult, fieldValues, result, userPayload);
            }
        }
        else if (await entityObject.InnerLoad(pk)) {
            await this.InnerSyncSingleItemUpdate(entityObject, fieldValues, result, userPayload);
        }
        else {
            result.ErrorMessage = 'Failed to load the item, it is possible the record with the specified primary key does not exist';
        }
    }
    async InnerSyncSingleItemUpdate(entityObject, fieldValues, result, userPayload) {
        entityObject.SetMany(fieldValues);
        if (await entityObject.Save()) {
            result.Success = true;
            if (!result.PrimaryKey || result.PrimaryKey.KeyValuePairs.length === 0) {
                result.PrimaryKey = new CompositeKey(entityObject.PrimaryKeys.map((pk) => ({ FieldName: pk.Name, Value: pk.Value })));
            }
            result.RecordJSON = await entityObject.GetDataObjectJSON({
                includeRelatedEntityData: false,
                excludeFields: [],
                omitEmptyStrings: false,
                relatedEntityList: [],
                omitNullValues: false,
                oldValues: false
            });
        }
        else {
            result.ErrorMessage = 'Failed to update the item :' + entityObject.LatestResult.CompleteMessage;
        }
    }
}
__decorate([
    RequireSystemUser(),
    Mutation(() => SyncDataResultType),
    __param(0, Arg('items', () => [ActionItemInputType])),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array, Object]),
    __metadata("design:returntype", Promise)
], SyncDataResolver.prototype, "SyncData", null);
//# sourceMappingURL=SyncDataResolver.js.map