"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.generateModelDecoder = generateModelDecoder;
const Generator = __importStar(require("@postxl/generator"));
const FlattenedFieldHelper = __importStar(require("./excel-field-decoder.helper"));
const FieldHelper = __importStar(require("./field-decoder.helper"));
function generateModelDecoder({ model, context }) {
    const imports = Generator.ImportGenerator.from(model.decoder.decoder.zodDecoder.location).add(model.types);
    const sharedParams = { model, context, imports };
    // We cannot inline below definitions, as we need to add the imports before
    const decoder = getDecoder(sharedParams);
    const flattenedParams = {
        ...sharedParams,
        toName: (field) => field.databaseName,
        codecDefinition: model.decoder.flattened,
    };
    const flattenedType = getFlattenedType(flattenedParams);
    const flattenedEncoder = getFlattenedEncoder(flattenedParams);
    const flattenedDecoder = getFlattenedDecoder(flattenedParams);
    const excelFlattenedParams = {
        ...sharedParams,
        toName: (field) => field.excelName,
        codecDefinition: model.decoder.excel,
    };
    const excelFlattenedType = getFlattenedType(excelFlattenedParams);
    const excelFlattenedEncoder = getFlattenedEncoder(excelFlattenedParams);
    const excelFlattenedDecoder = getFlattenedDecoder(excelFlattenedParams);
    return /* ts */ `
 import * as z from 'zod'
 ${imports.generate()}
 
 ${decoder}
 
 ${flattenedType}
 
 ${flattenedEncoder}
 
 ${flattenedDecoder}
 
 ${excelFlattenedType}
 
 ${excelFlattenedEncoder}
 
 ${excelFlattenedDecoder}`;
}
function getDecoder(params) {
    const { model } = params;
    const decoder = model.decoder.decoder;
    const fieldDecoders = [];
    for (const field of model.fields.values()) {
        fieldDecoders.push(FieldHelper.getFieldDecoder({ field, ...params }));
    }
    return `
    export const ${decoder.zodDecoder.name} = z.object({
      ${fieldDecoders.join(',\n')}
    })

    /**
     * Decoder to ensure that the input is a valid ${model.types.name} object.
     */
    export function ${decoder.parseFunction.name}(item: ${model.types.name}): ${model.types.name} {
    return ${decoder.zodDecoder.name}.parse(item)
    }`;
}
function getFlattenedType({ model, codecDefinition }) {
    return `/**
    * The ${model.types.name} flattened to a column structure.
    */
    export type ${codecDefinition.decoder.rawInputType.name} = z.infer<typeof ${codecDefinition.decoder.rawInputValidation.name}>`;
}
function getFlattenedEncoder(params) {
    const { model, imports, codecDefinition, toName } = params;
    const fields = [];
    for (const field of model.fields.values()) {
        switch (field.kind) {
            case 'discriminatedUnion': {
                const du = model.decoder.discriminatedUnions.get(field.name);
                imports.add(du.flattened.encoder);
                fields.push(`...${du.flattened.encoder.name}(item.${field.name})`);
                break;
            }
            case 'enum':
                fields.push(`"${toName(field)}": item.${field.name}`);
                break;
            default:
                fields.push(`"${toName(field)}": item.${field.name}`);
                break;
        }
    }
    const inputType = getFlattenedEncoderInputType(params);
    return `
    /**
     * Encodes a ${model.types.name} object to a flattened object, where each
     * nested field is mapped to a top-level field. This allows serialization to a column structure.
     */
    export function ${codecDefinition.encoder.name}(item: ${inputType}) {
      return {
        ${fields.join(',\n')}
      }
    }`;
}
function getFlattenedEncoderInputType({ model, imports }) {
    const omits = [];
    for (const field of model.fields.values()) {
        if (field.kind !== 'scalar') {
            continue;
        }
        if (field.isCreatedAt || field.isUpdatedAt) {
            omits.push(`'${field.name}'`);
        }
    }
    if (omits.length === 0) {
        return model.types.name;
    }
    imports.addTypeImport({
        from: Generator.toPackageName('@postxl/utils'),
        items: [Generator.toTypeName('Optional')],
    });
    return `Optional<${model.types.name}, ${omits.join(' | ')}>`;
}
function getFlattenedDecoder(params) {
    const { model, codecDefinition, toName } = params;
    const decoder = codecDefinition.decoder;
    const fieldDecoders = [];
    const duDecoders = [];
    const fieldTransformers = [];
    let hasDiscriminatedUnion = false;
    for (const field of model.fields.values()) {
        const { rawDecoder, transformer = FieldHelper.defaultTransformer } = FlattenedFieldHelper.getFieldElements({
            field,
            ...params,
            enforceNullable: false,
        });
        if (field.kind === 'discriminatedUnion') {
            hasDiscriminatedUnion = true;
            duDecoders.push(rawDecoder);
            fieldTransformers.push(`${field.name}: ${transformer('item, ctx')}`);
        }
        else {
            fieldDecoders.push(`"${toName(field)}": ${rawDecoder}`);
            fieldTransformers.push(`${field.name}: ${transformer('item["' + toName(field) + '"]')}`);
        }
    }
    return `
    export const ${decoder.rawInputValidation.name} = z.object({
        ${fieldDecoders.join(',\n')}
      })${duDecoders.map((du) => `.merge(${du})`).join('\n')}

    export function ${decoder.transformer.name}(item: ${decoder.rawInputType.name}${hasDiscriminatedUnion ? ', ctx: z.RefinementCtx' : ''}): ${model.types.name} {
      return {
        ${fieldTransformers.join(',\n')}
      }
    }

    /**
     * Decoder that parses the flattened object and transforms it to the ${model.types.name} type.
     */
    export const ${decoder.zodDecoder.name} = ${decoder.rawInputValidation.name}.transform(${decoder.transformer.name})

    export function ${decoder.parseFunction.name}(input: ${decoder.rawInputType.name}): ${model.types.name} {
      return ${decoder.zodDecoder.name}.parse(input)
    }`;
}
//# sourceMappingURL=model-decoder.generator.js.map