import type {
BuildingBlockConfiguration,
DynamicFormBBConfiguration,
DynamicFormConfiguration,
PageConfiguration
} from '@allianz/taly-core/schemas';
import type { PagesJsonSchema } from '@allianz/taly-sdk';
import {
getJsonFilePathInDirectory,
getJsonFilePathsInDirectory,
SPLIT_JOURNEY_PATHS
} from '@allianz/taly-sdk/node';
import { parse, stringify } from 'comment-json';
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
interface DfEditRequest {
pageId: string;
buildingBlockId: string;
config: DynamicFormConfiguration;
}
interface ConfigDirectories {
configDirectory: string;
baseConfigDirectory?: string;
}
/**
* Updates the dynamic form configuration for a building block identified by `id`
* directly in the source configuration file(s) on disk.
*
* Supports classic journeys (pages.json), split journeys (pages/*.json),
* and multi-tenant journeys (tenant pages + base pages fallback).
*
* Uses `comment-json` to preserve JSONC comments when writing back.
*/
export function updateDynamicFormConfig(
{ configDirectory, baseConfigDirectory }: ConfigDirectories,
{ pageId, buildingBlockId, config }: DfEditRequest
): void {
// For multi-tenant: try tenant first, then fall back to base
if (baseConfigDirectory) {
const updatedInTenant = tryUpdateInDirectory(configDirectory, pageId, buildingBlockId, config);
if (updatedInTenant) return;
const updatedInBase = tryUpdateInDirectory(
baseConfigDirectory,
pageId,
buildingBlockId,
config
);
if (updatedInBase) return;
throw new Error(
`Building block "${buildingBlockId}" not found in page "${pageId}" in tenant config "${configDirectory}" or base config "${baseConfigDirectory}".`
);
}
const updated = tryUpdateInDirectory(configDirectory, pageId, buildingBlockId, config);
if (updated) return;
throw new Error(
`Building block "${buildingBlockId}" not found in page "${pageId}" in config directory "${configDirectory}".`
);
}
/**
* Tries to find and update the building block in all page config files within a directory.
* Returns true if the building block was found and updated.
*/
function tryUpdateInDirectory(
configDirectory: string,
pageId: string,
blockId: string,
newConfig: DynamicFormConfiguration
): boolean {
// Classic journey: single pages.json(c) at the root
const pagesFilePath = getJsonFilePathInDirectory(configDirectory, 'pages');
if (pagesFilePath) {
return updateBlockInClassicPagesFile(pagesFilePath, pageId, blockId, newConfig);
}
// Split journey: individual page files in pages/ subdirectory
const pagesDir = join(configDirectory, SPLIT_JOURNEY_PATHS.FOLDERS.PAGES);
const pageFiles = getJsonFilePathsInDirectory(pagesDir);
for (const pageFile of pageFiles) {
if (updateBlockInSplitPageFile(pageFile, pageId, blockId, newConfig)) {
return true;
}
}
return false;
}
/**
* Updates a dynamic form block in a classic pages.json(c) file.
* The file has a top-level `pages` array, each with a `blocks` array.
*/
function updateBlockInClassicPagesFile(
filePath: string,
pageId: string,
blockId: string,
newConfig: DynamicFormConfiguration
): boolean {
const content = readAndParse<PagesJsonSchema>(filePath);
if (!content.pages) return false;
const page = content.pages.find((p) => p.id === pageId);
if (!page) return false;
if (updateBlockInBlocksArray(page.blocks, blockId, newConfig)) {
writeBack(filePath, content);
return true;
}
return false;
}
/**
* Updates a dynamic form block in a split page file.
* Each file is a single page with a top-level `blocks` array.
*/
function updateBlockInSplitPageFile(
filePath: string,
pageId: string,
blockId: string,
newConfig: DynamicFormConfiguration
): boolean {
const content = readAndParse<PageConfiguration>(filePath);
if (!content.blocks || content.id !== pageId) return false;
if (updateBlockInBlocksArray(content.blocks, blockId, newConfig)) {
writeBack(filePath, content);
return true;
}
return false;
}
/**
* Searches for a block by id in the blocks array and updates its `form` property.
* Only updates blocks that have a `form` property (dynamic form building blocks).
* Returns true if a matching block was found and updated.
*/
function updateBlockInBlocksArray(
blocks: (BuildingBlockConfiguration | DynamicFormBBConfiguration)[] | undefined,
blockId: string,
newConfig: DynamicFormConfiguration
): boolean {
if (!blocks) return false;
const block = blocks.find((b) => b.id === blockId);
if (!block || !isDynamicFormBbConfig(block)) return false;
block.form = newConfig;
return true;
}
function isDynamicFormBbConfig(
block: BuildingBlockConfiguration | DynamicFormBBConfiguration
): block is DynamicFormBBConfiguration {
return Boolean((block as DynamicFormBBConfiguration).form);
}
function readAndParse<T>(filePath: string): T {
const raw = readFileSync(filePath, 'utf8');
return parse(raw) as T;
}
function writeBack(filePath: string, content: unknown): void {
writeFileSync(filePath, stringify(content, null, 2) + '\n', 'utf8');
}