"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.generateDiscriminatedUnionDecoder = generateDiscriminatedUnionDecoder;
const Generator = __importStar(require("@postxl/generator"));
const FlattenedFieldHelper = __importStar(require("./excel-field-decoder.helper"));
const FieldDecoder = __importStar(require("./field-decoder.helper"));
function generateDiscriminatedUnionDecoder({ context, model, discriminatedUnion, }) {
    const imports = Generator.ImportGenerator.from(model.decoder.decoder.zodDecoder.location);
    const params = { context, model, discriminatedUnion, field: discriminatedUnion.field, imports };
    // We cannot inline below definitions, as we need to add the imports before
    const decoder = getDecoder(params);
    const flattenedDecoder = getFlattenedDecoder(params);
    return /* ts */ `
import * as z from 'zod'
${imports.generate()}

${decoder}

${getFlattenedType({ discriminatedUnion })}

${getFlattenedEncoder({ discriminatedUnion })}

${flattenedDecoder}`;
}
function getDecoder({ context, model, discriminatedUnion, field, imports }) {
    imports.addTypeImport({ from: context.types.location, items: [field.type] });
    const sharedFields = [];
    for (const f of field.commonFields.values()) {
        sharedFields.push(FieldDecoder.getFieldDecoder({ context, model, field: f, imports }));
    }
    const members = [];
    for (const member of field.members.values()) {
        const memberFields = [`type: z.literal('${member.type}')`, ...sharedFields];
        for (const f of member.fields.values()) {
            memberFields.push(FieldDecoder.getFieldDecoder({ context, model, field: f, imports }));
        }
        members.push(`z.object({ ${memberFields.join(', ')}})`);
    }
    return `
export const ${discriminatedUnion.decoder.zodDecoder.name} = z.discriminatedUnion('type', [
  ${members.join(',\n')}
])

/**
 * Decoder to ensure that the input is a valid ${field.type}.
 */
export function ${discriminatedUnion.decoder.parseFunction.name}(item: ${field.type}): ${field.type} {
 return ${discriminatedUnion.decoder.zodDecoder.name}.parse(item)
}
`;
}
function getFlattenedType({ discriminatedUnion }) {
    return `/**
 * The ${discriminatedUnion.field.type} discriminated union type flattened to a column structure.
 */
export type ${discriminatedUnion.flattened.decoder.rawInputType.name} = z.infer<typeof ${discriminatedUnion.flattened.decoder.rawInputValidation.name}>
`;
}
function getFlattenedEncoder({ discriminatedUnion, }) {
    return `
/**
 * Encodes a ${discriminatedUnion.field.type} discriminated union to a flattened object, where each
 * nested field is mapped to a top-level field. This allows serialization to a column structure.
 */
export function ${discriminatedUnion.flattened.encoder.name}(item: ${discriminatedUnion.field.type}) {
  return {
    ${getEncoderFields({ field: discriminatedUnion.field }).join(',\n')}
  }
}
`;
}
function getEncoderFields({ field }) {
    const result = [];
    result.push(`${field.name}: item.type`);
    for (const f of field.commonFields.values()) {
        result.push(`${f.databaseName}: item.${f.name}`);
    }
    for (const member of field.members.values()) {
        for (const f of member.fields.values()) {
            result.push(`${f.databaseName}: item.type === '${member.type}' ? item.${f.name} : null`);
        }
    }
    return result;
}
function getFlattenedDecoder({ context, model, discriminatedUnion, field, imports }) {
    imports.addTypeImport({ from: context.types.location, items: [field.type] });
    const fieldRawDecoders = [
        `${field.databaseName}: z.enum([${Array.from(field.members.values())
            .map((f) => `'${f.type}'`)
            .join(',')}]).transform((v) => v as string).nullable()`,
    ];
    const commonFieldRequiredChecks = [`nullCheck('${field.databaseName}')`];
    const commonFieldTransformers = [];
    const transformerSwitchCases = [];
    for (const f of field.commonFields.values()) {
        const { rawDecoder, transformer = FieldDecoder.defaultTransformer } = FlattenedFieldHelper.getFieldElements({
            field: f,
            context,
            model,
            imports,
            // All fields are mode nullable even if required within a discriminated union, as the DU itself might be nullable!
            enforceNullable: true,
        });
        fieldRawDecoders.push(`${f.databaseName}: ${rawDecoder}`);
        if (field.isRequired) {
            commonFieldRequiredChecks.push(`if (item.${f.databaseName} === null) { 
        ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Field ${f.databaseName} is required but null!', path: ['${f.databaseName}'] }) 
        return z.NEVER
      }`);
        }
        commonFieldTransformers.push(`${f.name}: ${transformer('item.' + f.databaseName + (f.isRequired ? '!' : ''))}`);
    }
    for (const member of field.members.values()) {
        const memberTransformers = [`type: '${member.type}'`, ...commonFieldTransformers];
        const memberFieldRequiredChecks = [];
        for (const f of member.fields.values()) {
            const { rawDecoder, transformer = FieldDecoder.defaultTransformer } = FlattenedFieldHelper.getFieldElements({
                field: f,
                context,
                model,
                imports,
                enforceNullable: true,
            });
            fieldRawDecoders.push(`${f.databaseName}: ${rawDecoder}`);
            if (f.isRequired) {
                memberFieldRequiredChecks.push(`nullCheck('${f.databaseName}')`);
            }
            memberTransformers.push(`${f.name}: ${transformer('item.' + f.databaseName + (f.isRequired ? '!' : ''))}`);
        }
        transformerSwitchCases.push(`case '${member.type}': {
        ${memberFieldRequiredChecks.join('\n')}
        return {
        ${memberTransformers.join(',\n')}
      }
    }`);
    }
    return `  
export const ${discriminatedUnion.flattened.decoder.rawInputValidation.name} = z.object({
    ${fieldRawDecoders.join(',\n')}
  })

export function ${discriminatedUnion.flattened.decoder.transformer.name}(item: ${discriminatedUnion.flattened.decoder.rawInputType.name}, ctx: z.RefinementCtx): ${field.type} {
  try {
    const nullCheck = (fieldName: keyof ${discriminatedUnion.flattened.decoder.rawInputType.name}) => {
      if (item[fieldName] === null) {
        const message = \`Field \${fieldName} is required but null!\`
        ctx.addIssue({ code: z.ZodIssueCode.custom, message, path: [fieldName] })
        throw new Error(message)
      }
    }

    ${commonFieldRequiredChecks.join('\n')}
    switch (item.${field.name}) {
      ${transformerSwitchCases.join('\n')}
      default: {
        const message = \`Field ${field.name} is not a valid enum value!\`
        ctx.addIssue({ code: z.ZodIssueCode.custom, message, path: ['${field.name}'] })
        throw new Error(message)
      }
    }
  } catch {
    return z.NEVER
  }
}

/**
 * Decoder that parses the flattened object and transforms it to the ${field.type} discriminated union.
 */
export const ${discriminatedUnion.flattened.decoder.zodDecoder.name} = ${discriminatedUnion.flattened.decoder.rawInputValidation.name}.transform(${discriminatedUnion.flattened.decoder.transformer.name})
`;
}
//# sourceMappingURL=discriminated-union.decoder.generator.js.map