"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateModelUpdateService = generateModelUpdateService;
const Generator = __importStar(require("@postxl/generator"));
const generator_1 = require("@postxl/generator");
const utils_1 = require("@postxl/utils");
function generateModelUpdateService({ model, context, }) {
    const imports = Generator.ImportGenerator.from(model.update.service.location)
        .addType(model.types)
        .addType(context.actions.execution.interface)
        .add(context.update.service)
        .add(context.view.service)
        .addImport({
        from: Generator.toPackageName('@postxl/utils'),
        items: [Generator.toFunctionName('ExhaustiveSwitchCheck')],
    });
    if (model.repository.kind === 'NoRepository') {
        return `TBD`; //generateModelUpdateService_NoRepository({ model, context, imports })
    }
    imports.add(model.repository);
    const actions = [];
    for (const action of model.update.action.actions) {
        actions.push(generateActionBlock({ action, model, context, imports }));
    }
    return `
    import { Inject, Injectable, forwardRef } from '@nestjs/common'
    import { z } from 'zod'

    ${imports.generate()}

    export type ${model.update.action.type.name} = ${actions.map((a) => a.type).join('| ')}

    export type ${model.update.action.resultTypeMap.name} = {
      ${actions.map((a) => `${a.action.name}: ${a.action.result.name}`).join(',\n')}
    }

    ${actions.map((a) => a.typeDefinition).join('\n')}

    ${actions.map((a) => a.inputDecoderAndType).join('\n')}
    
    ${actions
        .filter((a) => a.resultTypeDefinition !== null)
        .map((a) => a.resultTypeDefinition)
        .join('\n')}

    @Injectable()
    export class ${model.update.service.name} {


      constructor(
        private readonly data: ${model.repository.name},
        
        @Inject(forwardRef(() => ${context.update.service.name})) 
          private readonly updateService: ${context.update.service.name},

        @Inject(forwardRef(() => ${context.view.service.name})) 
          private readonly viewService: ${context.view.service.name}
      ) {
      }

      /**
       * Will be called by the dispatcher service to dispatch the given action.
       */
      public async dispatch<A extends ${model.update.action.type.name}>({ action, execution }: { action: A; execution: ${context.actions.execution.interface.name} }): Promise<${model.update.action.resultTypeMap.name}[A['type']]> {
        switch (action.type) {
          ${actions.map((a) => a.dispatchCase).join('\n')}

          default:
            throw new ExhaustiveSwitchCheck(action)
        }
      }

      ${actions.map((a) => a.serviceMethod).join('\n\n')}
    }
 `;
}
function generateZodModelCreateDecoder({ model, context, imports }) {
    const fields = Array.from(model.fields.values());
    const fieldDecoders = fields
        .filter((field) => !field.isReadonly)
        .map((field) => getZodFieldDecoder({ field, context, imports, optionalId: true }));
    return /* ts */ `z.object({
      ${fieldDecoders.join(',\n')}
    })`;
}
function generateZodModelUpdateDecoder({ model, context, imports }) {
    const fields = Array.from(model.fields.values());
    const fieldDecoders = fields
        .filter((field) => !field.isReadonly || field.kind === 'id')
        .map((field) => getZodFieldDecoder({ field, context, imports, optionalId: false }));
    return /* ts */ `z.object({
      ${fieldDecoders.join(',\n')}
    })`;
}
function generateZodModelCloneDecoder({ model, context, imports }) {
    const fields = Array.from(model.fields.values());
    const fieldDecoders = fields
        .filter((field) => field.kind !== 'id') // Exclude ID field - it will be auto-generated
        .map((field) => {
        // All fields are optional in clone overrides (including readonly fields)
        const decoder = getZodFieldDecoder({ field, context, imports, optionalId: true });
        return `${decoder}.optional()`;
    });
    return /* ts */ `z.object({
      id: ${model.types.id.decoder.name},
      overrides: z.object({
        ${fieldDecoders.join(',\n')}
      }).optional()
    })`;
}
function generateZodModelUpdateFieldDecoder({ model, context, imports }) {
    const fields = Array.from(model.fields.values());
    const updatableFields = fields.filter((field) => !field.isReadonly && field.kind !== 'id');
    if (updatableFields.length === 0) {
        // If there are no updatable fields, return a never type or minimal decoder
        return /* ts */ `z.never()`;
    }
    // Generate discriminated union members for each updatable field
    const unionMembers = updatableFields.map((field) => {
        const valueDecoder = getZodFieldValueDecoder({ field, context, imports });
        return `z.object({ id: ${model.types.id.decoder.name}, field: z.literal('${field.name}'), value: ${valueDecoder} })`;
    });
    return `z.discriminatedUnion('field', [
    ${unionMembers.join(',\n')}
  ])`;
}
/**
 * Returns just the value decoder for a field (without the field name prefix)
 */
function getZodFieldValueDecoder({ field, context, imports, }) {
    if (field.kind === 'discriminatedUnion') {
        return getZodFieldDiscriminatedUnionValueDecoder({ field, context, imports });
    }
    const parsers = getZodFieldDatabaseNativeParsers({ field: field, context, imports });
    if (!field.isRequired) {
        parsers.push(`nullable()`);
    }
    return parsers.join('.');
}
function getZodFieldDiscriminatedUnionValueDecoder({ field, context, imports, }) {
    const memberDecoders = [];
    for (const member of field.members.values()) {
        const memberFieldsDecoders = [`type: z.literal('${member.type}')`];
        for (const f of field.commonFields.values()) {
            memberFieldsDecoders.push(getZodFieldDatabaseNativeDecoder({ field: f, context, imports, optionalId: false }));
        }
        for (const f of member.fields.values()) {
            memberFieldsDecoders.push(getZodFieldDatabaseNativeDecoder({ field: f, context, imports, optionalId: false }));
        }
        memberDecoders.push(`z.object({ ${memberFieldsDecoders.join(',\n')} })`);
    }
    return `z.discriminatedUnion("type", [
    ${memberDecoders.join(',\n')}
  ])`;
}
/**
 * Returns the string defining the zod decoder for a given field.
 */
function getZodFieldDecoder({ field, context, imports, optionalId, }) {
    if (field.kind === 'discriminatedUnion') {
        return getZodFieldDiscriminatedUnionDecoder({ field, context, imports });
    }
    return getZodFieldDatabaseNativeDecoder({ field, context, imports, optionalId });
}
function getZodFieldDatabaseNativeDecoder({ field, context, imports, optionalId, }) {
    const parsers = getZodFieldDatabaseNativeParsers({ field, context, imports });
    if (!field.isRequired) {
        parsers.push(`nullable()`);
    }
    if (field.kind === 'id' && optionalId) {
        parsers.push(`optional()`);
    }
    return `${field.name}: ${parsers.join('.')}`;
}
function getZodFieldDatabaseNativeParsers({ field, context, imports, }) {
    switch (field.kind) {
        case 'id':
            return getZodFieldParsersForId({ field, context, imports });
        case 'scalar':
            return getZodFieldParsersForScalar(field);
        case 'relation':
            return getZodFieldParsersForRelation({ field, context, imports });
        case 'enum':
            return getZodParsersForEnum({ field, context });
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field);
    }
}
function getZodFieldParsersForId({ field, context, imports, }) {
    const parsers = [];
    // TODO: Adjust for number types
    switch (field.idType) {
        case 'Increment': {
            parsers.push('z.number()');
            break;
        }
        case 'UUID': {
            parsers.push('z.string()');
            break;
        }
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field.idType);
    }
    const model = context.models.get(field.modelName);
    parsers.push(`transform((v) => ${model.types.id.toBranded.name}(v))`);
    imports.add(model.types.id.toBranded);
    return parsers;
}
function getZodFieldParsersForScalar(field) {
    const parsers = [];
    switch (field.type) {
        case 'number': {
            parsers.push('z.number()');
            if (field.validations.type === 'int' || field.databaseType === 'Int' || field.databaseType === 'BigInt') {
                parsers.push('int()');
            }
            if (field.validations.min !== undefined) {
                parsers.push(`min(${field.validations.min})`);
            }
            if (field.validations.max !== undefined) {
                parsers.push(`max(${field.validations.max})`);
            }
            break;
        }
        case 'string': {
            parsers.push('z.string()');
            if (field.validations.maxLength) {
                parsers.push(`max(${field.validations.maxLength})`);
            }
            break;
        }
        case 'boolean': {
            parsers.push('z.boolean()');
            break;
        }
        case 'Date': {
            parsers.push('z.date()');
            break;
        }
        default: {
            throw new utils_1.ExhaustiveSwitchCheck(field);
        }
    }
    return parsers;
}
function getZodFieldParsersForRelation({ field, context, imports, }) {
    const parsers = ['z.string()'];
    const model = context.models.get(field.referencedModelName);
    parsers.push(`transform((v) => ${model.types.id.toBranded.name}(v))`);
    imports.add(model.types.id.toBranded);
    return parsers;
}
function getZodParsersForEnum({ field, context }) {
    const enum_ = context.enums.get(field.enumName);
    return [
        `z.enum([${Array.from(enum_.members.values())
            .map((v) => `'${v.value}'`)
            .join(', ')}])`,
    ];
}
function getZodFieldDiscriminatedUnionDecoder({ field, context, imports, }) {
    const memberDecoders = [];
    for (const member of field.members.values()) {
        const memberFieldsDecoders = [`type: z.literal('${member.type}')`];
        for (const f of field.commonFields.values()) {
            memberFieldsDecoders.push(getZodFieldDatabaseNativeDecoder({ field: f, context, imports, optionalId: false }));
        }
        for (const f of member.fields.values()) {
            memberFieldsDecoders.push(getZodFieldDatabaseNativeDecoder({ field: f, context, imports, optionalId: false }));
        }
        memberDecoders.push(`z.object({ ${memberFieldsDecoders.join(',\n')} })`);
    }
    return `${field.name}: z.discriminatedUnion("type", [
    ${memberDecoders.join(',\n')}
  ])`;
}
function generateActionBlock(params) {
    switch (params.action.kind) {
        case 'custom':
            return generateActionBlock_Custom(params);
        case 'standard':
            return generateActionBlock_Standard(params);
        default:
            throw new utils_1.ExhaustiveSwitchCheck(params.action);
    }
}
function generateActionBlock_Custom(params) {
    const { action, model, context } = params;
    return {
        action,
        type: action.type.name,
        typeDefinition: generateActionType(params),
        inputDecoderAndType: Generator.ts(
        //
        `export const ${action.payload.decoder.name} = z.object({
        // TODO: To be populated with correct decoder!
        id: ${model.types.id.decoder.name},
      })
      export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`),
        resultTypeDefinition: Generator.ts(
        //
        `// TODO: To be populated with correct type!
      export type ${action.result.name} = ${model.types.name}`),
        serviceMethod: Generator.ts(
        //
        `${(0, generator_1.toJsDocComment)(action.description)}
      public async ${action.name}({ data, execution: _ }: {data: ${action.payload.type.name}, execution: ${context.actions.execution.interface.name}}): Promise<${action.result.name}> {
        // TODO: Implement ${action.name} method!
        const item = await this.data.get(data.id)
        if (!item) {
          throw new Error(\`${model.userFriendlyName} with id \${data.id} not found\`)
        }
        return item
      }`),
        dispatchCase: generateActionDispatcherCase(params),
    };
}
function generateActionBlock_Standard(params) {
    return {
        action: params.action,
        type: params.action.type.name,
        typeDefinition: generateActionType(params),
        inputDecoderAndType: generateActionDecoder_Standard(params),
        resultTypeDefinition: generateActionResultType_Standard(params),
        serviceMethod: generateActionServiceMethod_Standard(params),
        dispatchCase: generateActionDispatcherCase(params),
    };
}
function generateActionType({ action, model }) {
    return Generator.ts(`${(0, generator_1.toJsDocComment)(action.description)}
  export type ${action.type.name} = { 
    scope: '${model.update.action.scope}'
    type: '${action.name}'
    payload: ${action.payload.type.name}
  }`);
}
function generateActionDispatcherCase({ action, model }) {
    return Generator.ts(`case '${action.name}':
        return this.${action.name}({ data: action.payload, execution }) as Promise<${model.update.action.resultTypeMap.name}[A['type']]>`);
}
function generateActionDecoder_Standard(params) {
    switch (params.action.name) {
        case 'create':
            return generateActionDecoder_Standard_Create(params);
        case 'createMany':
            return generateActionDecoder_Standard_CreateMany(params);
        case 'update':
            return generateActionDecoder_Standard_Update(params);
        case 'updateMany':
            return generateActionDecoder_Standard_UpdateMany(params);
        case 'updateField':
            return generateActionDecoder_Standard_UpdateField(params);
        case 'upsert':
            return generateActionDecoder_Standard_Upsert(params);
        case 'upsertMany':
            return generateActionDecoder_Standard_UpsertMany(params);
        case 'delete':
            return generateActionDecoder_Standard_Delete(params);
        case 'deleteMany':
            return generateActionDecoder_Standard_DeleteMany(params);
        case 'clone':
            return generateActionDecoder_Standard_Clone(params);
        case 'cloneMany':
            return generateActionDecoder_Standard_CloneMany(params);
        default:
            throw new utils_1.ExhaustiveSwitchCheck(params.action.name);
    }
}
function generateActionDecoder_Standard_Create(params) {
    const { action, model } = params;
    const createDecoder = generateZodModelCreateDecoder(params);
    return Generator.ts(`/**
    * Zod decoder for validating the create input of a ${model.userFriendlyName}.
    */
    export const ${action.payload.decoder.name} = ${createDecoder}

    export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_CreateMany(params) {
    const { action, model } = params;
    const actions = model.update.action.standardActions;
    // In case we do not expose a create action, we need to name and provide the create decoder
    const singleDecoderName = actions.create?.payload.decoder.name ?? `create${model._conjugated.PascalCase}`;
    const singleDecoder = actions.create === undefined ? `const ${singleDecoderName} = ${generateZodModelCreateDecoder(params)}` : '';
    return Generator.ts(`${singleDecoder}
    /**
     * Zod decoder for validating the createMany input of a ${model.userFriendlyName}.
     */
      export const ${action.payload.decoder.name} = z.array(${singleDecoderName})

      export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_Update(params) {
    const { action, model } = params;
    const updateDecoder = generateZodModelUpdateDecoder(params);
    return Generator.ts(`/**
    * Zod decoder for validating the update input of a ${model.userFriendlyName}.
    */
    export const ${action.payload.decoder.name} = ${updateDecoder}

    export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_UpdateMany(params) {
    const { action, model } = params;
    const actions = model.update.action.standardActions;
    // In case we do not expose a update action, we need to name and provide the update decoder
    const singleDecoderName = actions.update?.payload.decoder.name ?? `update${model._conjugated.PascalCase}`;
    const singleDecoder = actions.update === undefined ? `const ${singleDecoderName} = ${generateZodModelUpdateDecoder(params)}` : '';
    return Generator.ts(`${singleDecoder}
    /**
     * Zod decoder for validating the updateMany input of a ${model.userFriendlyName}.
     */
      export const ${action.payload.decoder.name} = z.array(${singleDecoderName})

      export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_UpdateField(params) {
    const { action, model } = params;
    const updateFieldDecoder = generateZodModelUpdateFieldDecoder(params);
    return Generator.ts(`/**
    * Zod decoder for validating the updateField input of a ${model.userFriendlyName}.
    */
    export const ${action.payload.decoder.name} = ${updateFieldDecoder}

    export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_Upsert(params) {
    const { action, model } = params;
    const actions = model.update.action.standardActions;
    const createDecoderName = actions.create?.payload.decoder.name ?? `create${model._conjugated.PascalCase}`;
    const createDecoder = actions.create === undefined ? `const ${createDecoderName} = ${generateZodModelCreateDecoder(params)}` : '';
    const updateDecoderName = actions.update?.payload.decoder.name ?? `update${model._conjugated.PascalCase}`;
    const updateDecoder = actions.update === undefined ? `const ${updateDecoderName} = ${generateZodModelUpdateDecoder(params)}` : '';
    return Generator.ts(`${createDecoder}
    ${updateDecoder}
    /**
     * Zod decoder for validating the upsert input of a ${model.userFriendlyName}.
     */
      export const ${action.payload.decoder.name} = z.union([${createDecoderName},${updateDecoderName}])

      export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_UpsertMany(params) {
    const { action, model } = params;
    const actions = model.update.action.standardActions;
    const createDecoderName = actions.create?.payload.decoder.name ?? `create${model._conjugated.PascalCase}`;
    const createDecoder = actions.create === undefined ? `const ${createDecoderName} = ${generateZodModelCreateDecoder(params)}` : '';
    const updateDecoderName = actions.update?.payload.decoder.name ?? `update${model._conjugated.PascalCase}`;
    const updateDecoder = actions.update === undefined ? `const ${updateDecoderName} = ${generateZodModelUpdateDecoder(params)}` : '';
    const singleDecoderName = actions.upsert?.payload.decoder.name ?? `upsert${model._conjugated.PascalCase}`;
    const singleDecoder = actions.upsert === undefined
        ? `const ${singleDecoderName} = z.union([${createDecoderName},${updateDecoderName}])`
        : '';
    return Generator.ts(`${createDecoder}
    ${updateDecoder}
    ${singleDecoder}
    /**
     * Zod decoder for validating the upsertMany input of a ${model.userFriendlyName}.
     */
      export const ${action.payload.decoder.name} = z.array(${singleDecoderName})

      export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_Delete(params) {
    const { action, model, imports } = params;
    imports.add(model.types.id.decoder);
    imports.addType(model.types.id);
    return Generator.ts(`/**
    * Zod decoder for validating the delete input of a ${model.userFriendlyName}.
    */
    export const ${action.payload.decoder.name} = ${model.types.id.decoder.name}

    export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_DeleteMany(params) {
    const { action, model, imports } = params;
    imports.add(model.types.id.decoder);
    imports.addType(model.types.id);
    return Generator.ts(`/**
    * Zod decoder for validating the deleteMany input of a ${model.userFriendlyName}.
    */
    export const ${action.payload.decoder.name} = z.array(${model.types.id.decoder.name})

    export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_Clone(params) {
    const { action, model, imports } = params;
    imports.add(model.types.id.decoder);
    const cloneDecoder = generateZodModelCloneDecoder(params);
    return Generator.ts(`/**
    * Zod decoder for validating the clone input of a ${model.userFriendlyName}.
    */
    export const ${action.payload.decoder.name} = ${cloneDecoder}

    export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionDecoder_Standard_CloneMany(params) {
    const { action, model, imports } = params;
    const actions = model.update.action.standardActions;
    imports.add(model.types.id.decoder);
    // In case we do not expose a clone action, we need to name and provide the clone decoder
    const singleDecoderName = actions.clone?.payload.decoder.name ?? `clone${model._conjugated.PascalCase}`;
    const singleDecoder = actions.clone === undefined ? `const ${singleDecoderName} = ${generateZodModelCloneDecoder(params)}` : '';
    return Generator.ts(`${singleDecoder}
    /**
     * Zod decoder for validating the cloneMany input of a ${model.userFriendlyName}.
     */
      export const ${action.payload.decoder.name} = z.array(${singleDecoderName})

      export type ${action.payload.type.name} = z.infer<typeof ${action.payload.decoder.name}>`);
}
function generateActionResultType_Standard({ action, model }) {
    switch (action.name) {
        case 'create':
        case 'update':
        case 'updateField':
        case 'upsert':
        case 'clone':
            return Generator.ts(`export type ${action.result.name} = ${model.types.name}`);
        case 'createMany':
        case 'updateMany':
        case 'upsertMany':
        case 'cloneMany':
            return Generator.ts(`export type ${action.result.name} = ${model.types.name}[]`);
        case 'delete':
            return Generator.ts(`export type ${action.result.name} = ${model.types.id.name}`);
        case 'deleteMany':
            return Generator.ts(`export type ${action.result.name} = ${model.types.id.name}[]`);
        default:
            throw new utils_1.ExhaustiveSwitchCheck(action.name);
    }
}
function generateActionServiceMethod_Standard(params) {
    const { action, context } = params;
    return Generator.ts(`${(0, generator_1.toJsDocComment)(action.description)}
  public async ${action.name}({ data, execution }: {
    data: ${action.payload.type.name};
    execution: ${context.actions.execution.interface.name}
  }): Promise<${action.result.name}> {
    ${generateActionServiceMethod_StandardImplementation(params)}
  }`);
}
function generateActionServiceMethod_StandardImplementation(params) {
    switch (params.action.name) {
        case 'create':
            return generateActionServiceMethod_Standard_Create(params);
        case 'createMany':
            return generateActionServiceMethod_Standard_CreateMany(params);
        case 'update':
            return generateActionServiceMethod_Standard_Update(params);
        case 'updateMany':
            return generateActionServiceMethod_Standard_UpdateMany(params);
        case 'updateField':
            return generateActionServiceMethod_Standard_UpdateField(params);
        case 'upsert':
            return generateActionServiceMethod_Standard_Upsert(params);
        case 'upsertMany':
            return generateActionServiceMethod_Standard_UpsertMany(params);
        case 'delete':
            return generateActionServiceMethod_Standard_Delete(params);
        case 'deleteMany':
            return generateActionServiceMethod_Standard_DeleteMany(params);
        case 'clone':
            return generateActionServiceMethod_Standard_Clone(params);
        case 'cloneMany':
            return generateActionServiceMethod_Standard_CloneMany(params);
        default:
            throw new utils_1.ExhaustiveSwitchCheck(params.action.name);
    }
}
function generateActionServiceMethod_Standard_Create(_) {
    return `
    return this.data.create({ item: data, execution })`;
}
function generateActionServiceMethod_Standard_CreateMany(_) {
    return `
    return this.data.createMany({ items: data, execution })`;
}
function generateActionServiceMethod_Standard_Update(_) {
    return `
    return this.data.update({ item: data, execution })`;
}
function generateActionServiceMethod_Standard_UpdateMany(_) {
    return `
    return this.data.updateMany({ items: data, execution })`;
}
function generateActionServiceMethod_Standard_UpdateField(_) {
    return `
    // Fetch the current entity
    const existing = await this.data.get(data.id)
    if (!existing) {
      throw new Error(\`${_.model.userFriendlyName} with id \${data.id} not found\`)
    }

    // Build partial update object with just the single field
    const updateData: any = {
      id: data.id,
      [data.field]: data.value
    }

    return this.data.update({ item: updateData, execution })`;
}
function generateActionServiceMethod_Standard_Upsert(_) {
    return `
    return this.data.upsert({ item: data, execution })`;
}
function generateActionServiceMethod_Standard_UpsertMany(_) {
    return `
    return this.data.upsertMany({ items: data, execution })`;
}
function generateActionServiceMethod_Standard_Delete(_) {
    return `
    return this.data.delete({ id: data, execution })`;
}
function generateActionServiceMethod_Standard_DeleteMany(_) {
    return `
    return this.data.deleteMany({ ids: data, execution })`;
}
function generateActionServiceMethod_Standard_Clone({ model, context, imports }) {
    const fields = Array.from(model.fields.values());
    const labelField = model.labelField;
    const uniqueFields = fields.filter((f) => f.isUnique && f.kind !== 'id');
    const childrenToClone = [];
    for (const [, otherModel] of context.models) {
        const otherFields = Array.from(otherModel.fields.values());
        for (const field of otherFields) {
            if (field.kind === 'relation' && field.deepClone && field.referencedModelName === model.name) {
                if (otherModel.update.kind === 'skipped') {
                    throw new Error(`Model ${otherModel.name} has deepClone=true on field ${field.name} referencing ${model.name}, but ${otherModel.name} does not have update actions enabled`);
                }
                // Type assertion is safe because we've already checked that update.kind === 'exists'
                childrenToClone.push({ childModel: otherModel, relationField: field });
            }
        }
    }
    // Generate code to clone child entities that reference this entity
    const deepCloneStatements = childrenToClone
        .map(({ childModel, relationField }) => {
        // Convert field name to PascalCase for the method name (e.g., featuredPostId -> FeaturedPostId)
        const fieldNamePascalCase = (0, utils_1.toPascalCase)(relationField.name);
        return `
    // Deep clone all ${childModel.name} entities that reference this ${model.name} via ${relationField.name}
    const referencing${childModel.name}Entities = await this.viewService.${childModel.view.service.variableName}.data.getItemsFor${fieldNamePascalCase}(original.id)
    for (const child of referencing${childModel.name}Entities.values()) {
      await this.updateService.${childModel.update.service.variableName}.clone({
        data: { id: child.id, overrides: { ${relationField.name}: clonedEntity.id } },
        execution
      })
    }`;
    })
        .join('\n');
    const uniqueFieldHandling = generateUniqueFieldHandlingForClone({ uniqueFields, labelField, imports });
    return `
    // Fetch the original entity
    const original = await this.data.get(data.id)
    if (!original) {
      throw new Error(\`${model.userFriendlyName} with id \${data.id} not found\`)
    }

    // Create a copy of the entity
    const clonedData: any = { ...original }
    delete clonedData.id
    delete clonedData.createdAt
    delete clonedData.updatedAt

    // Apply overrides if provided
    if (data.overrides) {
      Object.assign(clonedData, data.overrides)
    }

    ${uniqueFieldHandling}

    // Create the new entity
    const clonedEntity = await this.data.create({ item: clonedData, execution })

    ${deepCloneStatements}

    return clonedEntity`;
}
function generateUniqueFieldHandlingForClone({ uniqueFields, labelField, imports, }) {
    const uniqueFieldHandling = [];
    if (labelField?.kind === 'scalar' && labelField.type === 'string') {
        uniqueFieldHandling.push(`
    // Handle label field and unique fields to ensure uniqueness
    const existingLabels = await this.data.getAllAsArray()
    const existingLabelValues = existingLabels.map((item: any) => item.${labelField.name})
    clonedData.${labelField.name} = ensureUnique(clonedData.${labelField.name} as string, existingLabelValues)
    `);
    }
    for (const field of uniqueFields) {
        if (field.kind === 'scalar' && field.type === 'string') {
            uniqueFieldHandling.push(`
    // Handle unique string field: ${field.name}
    if (clonedData.${field.name}) {
      const existingValues = await this.data.getAllAsArray()
      const existingFieldValues = existingValues.map((item: any) => item.${field.name})
      clonedData.${field.name} = ensureUnique(clonedData.${field.name} as string, existingFieldValues)
    }`);
        }
        else if (field.kind === 'scalar' && field.type === 'number') {
            uniqueFieldHandling.push(`
    // Handle unique number field: ${field.name}
    const nextId = await this.data.generateNextId()
    clonedData.${field.name} = nextId`);
        }
    }
    if (uniqueFieldHandling.length > 0) {
        imports.addImport({
            from: Generator.toPackageName('@postxl/utils'),
            items: [Generator.toFunctionName('ensureUnique')],
        });
    }
    return uniqueFieldHandling.join('\n');
}
function generateActionServiceMethod_Standard_CloneMany(_) {
    return `
    const results: ${_.model.types.name}[] = []
    for (const item of data) {
      const cloned = await this.clone({ data: item, execution })
      results.push(cloned)
    }
    return results`;
}
//# sourceMappingURL=model-update-service.generator.js.map