"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.generateModel = generateModel;
exports.getFieldDefinitions = getFieldDefinitions;
const Generator = __importStar(require("@postxl/generator"));
const utils_1 = require("@postxl/utils");
function generateModel({ model, context }) {
    const imports = Generator.ImportGenerator.from(model.types._filePath);
    imports.addImport({
        items: [Generator.toTypeName('CreateDto'), Generator.toTypeName('UpdateDto'), Generator.toTypeName('UpsertDto')],
        from: Generator.toFilePath('./dto.types'),
    });
    const fieldDefinitions = getFieldDefinitions({
        fields: Array.from(model.fields.values()),
        model,
        imports,
        context,
    });
    const types = model.types;
    return `
import { z } from 'zod'
${imports.generate()}

  ${Generator.toJsDocComment(model.description)}
export type ${types.name} = {
  ${fieldDefinitions}
}

${generateBrandedIdType(model)}

/**
 * Data transfer object for creating a new ${model.userFriendlyName} instance.
 */
export type ${types.dto.create.name} = CreateDto<${types.name}, ${types.id.name}>

/**
 * Data transfer object for updating an existing ${model.userFriendlyName} instance.
 */
export type ${types.dto.update.name} = UpdateDto<${types.name}, ${types.id.name}>

/**
 * Data transfer object for upserting a new or existing ${model.userFriendlyName} instance.
 */
export type ${types.dto.upsert.name} = UpsertDto<${types.name}, ${types.id.name}>

/**
 * Data transfer object for cloning a new ${model.userFriendlyName} instance.
 */
export type ${types.dto.clone.name} = { 
  ${Array.from(model.fields.values())
        .map((f) => {
        // Id field is always required to resolve the source.
        if (f.kind === 'id') {
            return `${f.name}: ${f.type}`;
        }
        if (f.isUnique) {
            //`unique` fields require a new value.
            if (f.isRequired) {
                return `${f.name}: ${f.type}`;
            }
            return `${f.name}: ${f.type} | null`;
        }
        // Non-unique fields can be copied from the source.
        if (f.isRequired) {
            return `${f.name}?: ${f.type}`;
        }
        return `${f.name}?: ${f.type} | null`;
    })
        .join(', ')} 
}
`;
}
function getFieldDefinitions({ fields, model, imports, context, }) {
    const fieldDefinitions = [];
    for (const field of fields) {
        const fieldDefinition = getFieldDefinition({ field, model, imports, context });
        fieldDefinitions.push(Generator.addJsDocComment(`${field.name}: ${fieldDefinition}`, field.description));
    }
    return fieldDefinitions.join('\n\n');
}
function getFieldDefinition({ field, model, imports, context, }) {
    switch (field.kind) {
        case 'id':
            return field.type;
        case 'scalar':
            return addNullable(field.type, field.isRequired);
        case 'enum': {
            const referencedEnum = context.enums.get(field.enumName);
            if (!referencedEnum) {
                throw new Error(`Enum ${field.enumName} not found`);
            }
            imports.addImport({ items: [referencedEnum.types.name], from: referencedEnum.types._filePath });
            return addNullable(referencedEnum.name, field.isRequired);
        }
        case 'relation': {
            const referencedModel = context.models.get(field.referencedModelName);
            if (!referencedModel) {
                throw new Error(`Model ${field.referencedModelName} not found`);
            }
            imports.addImport({ items: [referencedModel.types.id.name], from: referencedModel.types._filePath });
            return addNullable(referencedModel.types.id.name, field.isRequired);
        }
        case 'discriminatedUnion': {
            const du = model.types.discriminatedUnions.get(field.name);
            imports.add(du.type);
            return addNullable(field.type, field.isRequired);
        }
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field);
    }
}
function addNullable(type, isRequired) {
    return isRequired ? type : `${type} | null`; //NOSONAR
}
function generateBrandedIdType(model) {
    const { PascalCase } = model._conjugated;
    const decoder = Generator.toFunctionName(`z${PascalCase}Id`);
    const brandedType = Generator.toTypeName(`${PascalCase}Id`);
    const toBrandedFunction = Generator.toFunctionName(`to${PascalCase}Id`);
    return `  
/**
 * Zod decoder for validating a ${model.userFriendlyName} id.
 */
export const ${decoder} = z
  .string()
  .brand('PXL.${brandedType}')
  
/**
 * Branded Id type used to identify an instance of a ${model.userFriendlyName} model.
 */
export type ${brandedType} = z.infer<typeof ${decoder}>

/**
 * Converts a given scalar to a branded id type.
 */
export const ${toBrandedFunction} = (input: string) => ${decoder}.parse(input)`;
}
//# sourceMappingURL=model-type.generator.js.map