"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateDetectDeltaAllModels = generateDetectDeltaAllModels;
const generator_1 = require("@postxl/generator");
function generateDetectDeltaAllModels(context) {
    const { decoders, import: { detectDelta }, } = context;
    const { allModels } = detectDelta;
    const imports = generator_1.ImportGenerator
        //
        .from(allModels.location)
        .add(context.repositories.dataService)
        .addType(decoders.dataModel.decodedType)
        .add(context.import.detectDelta.types.generic.modelType)
        .addType(context.import.detectDelta.types.generic.errors.type)
        .add(context.import.detectDelta.types.generic.errors.typeGuard)
        .addImport({
        from: (0, generator_1.toPackageName)('@postxl/utils'),
        items: [(0, generator_1.toFunctionName)('red'), (0, generator_1.toFunctionName)('format'), (0, generator_1.toFunctionName)('pluralize')],
    });
    const { resultAssignments, idMapTypes, idMapInit, deltaTypes } = prepareModelDeltaTypes({ context, imports });
    return `
${imports.generate()}

/**
 * The delta of for all models.
 */
export type ${context.import.detectDelta.allModels.type.name} = {
  ${deltaTypes.join('\n')}
}

export type ${context.import.detectDelta.allModels.idMapType.name} = {
  ${idMapTypes.join('\n')}
}

function getIdMap(data: DataModel): ${context.import.detectDelta.allModels.idMapType.name} {
  const result: IdMap = {
    ${idMapInit.join(',\n')}
  }
  return result
}

function toIdMap<Model extends {id: ID}, ID extends string>(items?: Model[]): Map<ID, Model> {
  const result = new Map<ID, Model>()
  if (!items) return result
  for (const item of items) {
    result.set(item.id, item)
  }
  return result
}

export async function detectDelta(
  dataService: ${context.repositories.dataService.name},
  data: ${decoders.dataModel.decodedType.name},
  throwInCaseOfError = true,
): Promise<${allModels.type.name}> {
  const idMap = getIdMap(data)
  const result: ${allModels.type.name} = {
    ${resultAssignments.join(',\n')}
  }
  const errors = ${allModels.extractErrors.name}(result)
  if (Object.keys(errors).length > 0 && throwInCaseOfError) {
    throw new Error(${allModels.summarizeErrors.name}(errors))
  }
  return result
}

export function ${allModels.extractErrors.name}(delta: ${allModels.type.name}): Record<string, ${detectDelta.types.generic.errors.type.name}<any, any, any>[]> {
  const result: Record<string, ${detectDelta.types.generic.errors.type.name}<any, any, any>[]> = {}
  for (const [key, value] of Object.entries(delta)) {
    const errors = value?.filter((e: Delta_Model<any, any, any>) => ${detectDelta.types.generic.errors.typeGuard.name}(e)) as ${detectDelta.types.generic.errors.type.name}<
      any,
      any,
      any
    >[]
    if (errors && errors.length > 0) {
      result[key] = errors
    }
  }
  return result
}

export function ${allModels.summarizeErrors.name}(errors: Record<string, ${detectDelta.types.generic.errors.type.name}<any, any, any>[]>): string {
  let modelsWithErrors = 0
  let totalErrors = 0
  const modelCounts: string[] = []

  for (const [key, value] of Object.entries(errors)) {
    modelsWithErrors++
    totalErrors += value.length
    modelCounts.push(\`\${key}: \${format(value.length)} \${pluralize('error', value.length)}\`)
  }
  return (
    red(
      \`\${format(totalErrors)} \${pluralize('error', totalErrors)} in \${modelsWithErrors} \${pluralize('model', modelsWithErrors)} detected in delta!\`,
    ) +
    \`
\${modelCounts.join('\\n')}
Details:
\${JSON.stringify(errors, null, 2)}
\${red('Please fix the errors and try importing again!')}\`
  )
}
`;
}
function prepareModelDeltaTypes({ context, imports }) {
    const deltaTypes = [];
    const resultAssignments = [];
    const idMapTypes = [];
    const idMapInit = [];
    for (const model of context.models.values()) {
        imports
            //
            .addType(model.types)
            .addType(model.types.id)
            .add(model.import.delta.errors)
            .add(model.import.delta.detect);
        deltaTypes.push(`${model.decoder.dataModelEntry}?: ${context.import.detectDelta.types.generic.modelType.name}<
        ${model.types.name}, 
        ${model.types.id.name},
        ${model.import.delta.errors.name}
        >[]`);
        idMapTypes.push(`'${model.decoder.dataModelEntry}': Map<${model.types.id.name}, ${model.types.name}>`);
        idMapInit.push(`${model.decoder.dataModelEntry}: toIdMap(data.${model.decoder.dataModelEntry})`);
        resultAssignments.push(`${model.decoder.dataModelEntry}: await ${model.import.delta.detect.name}(dataService, data.${model.decoder.dataModelEntry} ?? [], idMap)`);
    }
    return { resultAssignments, deltaTypes, idMapTypes, idMapInit };
}
//# sourceMappingURL=detect-delta-all-models.generator.js.map