"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChangesetUtils = void 0;
const tslib_1 = require("tslib");
const JsonPatch = tslib_1.__importStar(require("fast-json-patch"));
const errors_1 = require("../types/errors");
const explore_1 = require("../types/explore");
const assertUnreachable_1 = tslib_1.__importDefault(require("./assertUnreachable"));
class ChangesetUtils {
    static applyChange(entity, change) {
        switch (change.type) {
            case 'create':
                throw new errors_1.ParameterError('Create change is not supported');
            case 'delete':
                throw new errors_1.ParameterError('Delete change is not supported');
            case 'update':
                if (!entity) {
                    return undefined;
                }
                const errors = JsonPatch.validate(change.payload.patches, entity);
                if (errors) {
                    throw new Error(`Invalid patches: ${errors.message}`);
                }
                const result = JsonPatch.applyPatch(entity, change.payload.patches);
                return result.newDocument;
            default:
                return (0, assertUnreachable_1.default)(change, 'Invalid change type');
        }
    }
    static applyChangeset(changeset, explores) {
        const changedExplores = Object.entries(explores).reduce((acc, [exploreName, explore]) => {
            if ((0, explore_1.isExploreError)(explore)) {
                return acc;
            }
            const relevantChanges = changeset.changes.filter((change) => explore.tables[change.entityTableName]);
            if (relevantChanges.length === 0) {
                return acc;
            }
            let patchedExplore = explore;
            for (const change of relevantChanges) {
                const tableName = change.entityTableName;
                switch (change.entityType) {
                    case 'table':
                        switch (change.type) {
                            case 'create':
                                throw new errors_1.NotImplementedError(`Not implemented: applyChange for table ${tableName} create change`);
                            case 'update':
                                const updatePatch = JsonPatch.applyPatch(patchedExplore, [
                                    ...(patchedExplore.baseTable ===
                                        change.entityName &&
                                        change.entityName in patchedExplore
                                        ? [
                                            {
                                                op: 'replace',
                                                path: `/`,
                                                value: this.applyChange(patchedExplore, change),
                                            },
                                        ]
                                        : []),
                                    {
                                        op: 'replace',
                                        path: `/tables/${change.entityName}`,
                                        value: this.applyChange(patchedExplore.tables[change.entityName], change),
                                    },
                                ]);
                                patchedExplore = updatePatch.newDocument;
                                break;
                            case 'delete':
                                throw new errors_1.NotImplementedError(`Not implemented: applyChange for table ${tableName} delete change`);
                            default:
                                return (0, assertUnreachable_1.default)(change, 'Invalid change type');
                        }
                        break;
                    case 'metric':
                    case 'dimension':
                        const entityType = `${change.entityType}s`;
                        switch (change.type) {
                            case 'create':
                                if (patchedExplore.tables[tableName][entityType][change.entityName]) {
                                    const error = new errors_1.ChangesetConflictError(`Entity "${change.entityName}" already exists in table "${tableName}" of explore`, {
                                        entityName: change.entityName,
                                        entityTableName: tableName,
                                    });
                                    // eslint-disable-next-line no-console
                                    console.warn(error);
                                    break;
                                }
                                const createPatch = JsonPatch.applyPatch(patchedExplore, [
                                    {
                                        op: 'replace',
                                        path: `/tables/${tableName}/${entityType}/${change.entityName}`,
                                        value: change.payload.value,
                                    },
                                ]);
                                patchedExplore = createPatch.newDocument;
                                break;
                            case 'update':
                                if (!patchedExplore.tables[tableName]) {
                                    break;
                                }
                                const existingEntity = patchedExplore.tables[tableName][entityType][change.entityName];
                                // Skip update if entity doesn't exist to avoid setting undefined values
                                if (!existingEntity) {
                                    break;
                                }
                                const updatePatch = JsonPatch.applyPatch(patchedExplore, [
                                    {
                                        op: 'replace',
                                        path: `/tables/${tableName}/${entityType}/${change.entityName}`,
                                        value: this.applyChange(existingEntity, change),
                                    },
                                ]);
                                patchedExplore = updatePatch.newDocument;
                                break;
                            case 'delete':
                                const deletePatch = JsonPatch.applyPatch(patchedExplore, [
                                    {
                                        op: 'remove',
                                        path: `/tables/${tableName}/${entityType}/${change.entityName}`,
                                    },
                                ]);
                                patchedExplore = deletePatch.newDocument;
                                break;
                            default:
                                return (0, assertUnreachable_1.default)(change, 'Invalid change type');
                        }
                        break;
                    default:
                        return (0, assertUnreachable_1.default)(change.entityType, 'Invalid entity type');
                }
            }
            acc[exploreName] = patchedExplore;
            return acc;
        }, {});
        const patchedExploreNamess = Object.keys(changedExplores);
        if (patchedExploreNamess.length > 0) {
            const patchResult = JsonPatch.applyPatch(explores, patchedExploreNamess.map((exploreName) => ({
                op: 'replace',
                path: `/${exploreName}`,
                value: changedExplores[exploreName],
            })));
            return patchResult.newDocument;
        }
        return explores;
    }
}
exports.ChangesetUtils = ChangesetUtils;
//# sourceMappingURL=changeset.js.map