"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSeedDataForModels = generateSeedDataForModels;
exports.generateModelSeedDataJson = generateModelSeedDataJson;
exports.generateModelSeedDataTypescript = generateModelSeedDataTypescript;
const faker_1 = require("@faker-js/faker");
const utils_1 = require("@postxl/utils");
function generateSeedDataForModels(context) {
    const fakers = new Map();
    const modelIds = new Map();
    for (const [modelName, model] of context.models) {
        const faker = new faker_1.Faker({ seed: model.faker?.seed ?? 0, locale: [faker_1.base, faker_1.en] });
        fakers.set(modelName, faker);
        modelIds.set(modelName, generateModelSeedIds({ faker, model }));
    }
    const models = new Map();
    for (const [modelName, model] of context.models) {
        const faker = fakers.get(modelName);
        const data = generateModelSeedData({ context, faker, modelIds, model });
        models.set(modelName, data);
    }
    return models;
}
function generateModelSeedIds({ faker, model }) {
    const ids = new Set();
    addSeedIds(ids, faker, model);
    addFakerIds(ids, faker, model);
    return Array.from(ids);
}
function addSeedIds(ids, faker, model) {
    if (!model.seed || model.seed.length === 0) {
        return;
    }
    for (const [index, seedEntry] of model.seed.entries()) {
        if (!seedEntry.id) {
            seedEntry.id = generateId({ faker, model, index });
        }
        if (ids.has(seedEntry.id)) {
            throw new Error(`Duplicate seed entry id: ${(0, utils_1.yellow)(seedEntry.id.toString())} for model ${(0, utils_1.yellow)(model.name)}`);
        }
        ids.add(seedEntry.id);
    }
}
function addFakerIds(ids, faker, model) {
    if (!model.faker?.items) {
        return;
    }
    for (let i = 0; i < model.faker.items; i++) {
        let id = generateId({ faker, model, index: i });
        let retries = 0;
        while (ids.has(id)) {
            id = generateId({ faker, model, index: i });
            retries++;
            if (retries > 10) {
                throw new Error(`Failed to generate unique ID after 10 attempts for model ${model.name}`);
            }
        }
        ids.add(id);
    }
}
function generateId({ faker, model, index }) {
    const field = model.idField;
    if (field.seed.type !== 'default') {
        return generateFromSeedConfig(faker, field.seed);
    }
    switch (field.idType) {
        case 'Increment':
            return index;
        case 'UUID':
            return `fake${model._conjugated.PascalCase}Id-${index}`;
        // return faker.string.uuid()
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field.idType);
    }
}
function generateFromSeedConfig(faker, seed) {
    switch (seed.type) {
        case 'default':
            throw new Error('Please handle default case outside!');
        case 'const':
            return seed.seed;
        case 'array':
            return faker.helpers.arrayElement(seed.seed);
        case 'faker':
            return fakeEval(faker, seed.faker);
        default:
            throw new utils_1.ExhaustiveSwitchCheck(seed);
    }
}
function fakeEval(faker, expression) {
    const exp = expression.replace('faker.', '').trim();
    return faker.helpers.fake(`{{${exp}}}`);
}
function generateModelSeedData({ context, faker, modelIds, model, }) {
    const data = [];
    if (model.seed && model.seed.length > 0) {
        data.push(...model.seed);
    }
    let index = data.length;
    const ownIds = modelIds.get(model.name);
    if (model.faker?.items) {
        for (let i = 0; i < model.faker.items; i++) {
            const id = ownIds[index];
            index++;
            data.push(generateSeedDataRecord({ context, faker, modelIds, model, id }));
        }
    }
    return data;
}
function generateSeedDataRecord({ context, faker, modelIds, model, id, }) {
    const data = {};
    for (const field of model.fields.values()) {
        if (field.kind === 'scalar' && (field.isCreatedAt || field.isUpdatedAt)) {
            continue;
        }
        data[field.name] = generateSeedDataField({ context, faker, modelIds, model, id, field });
    }
    return data;
}
function generateSeedDataField({ context, faker, modelIds, model, id, field, }) {
    try {
        if (field.kind === 'discriminatedUnion') {
            return generateDiscriminatedUnionField({ context, faker, modelIds, model, id, field });
        }
        else {
            return generateSeedDataFieldNative({ context, faker, modelIds, model, id, field });
        }
    }
    catch (error) {
        console.error(`Error generating seed data for field ${field.name} in model ${model.name}:`, error);
        return undefined;
    }
}
function generateSeedDataFieldNative({ context, faker, modelIds, model, id, field, }) {
    switch (field.kind) {
        case 'id':
            return id;
        case 'scalar':
            return generateScalarField({ faker, field });
        case 'relation':
            return generateRelationField({ faker, modelIds, model, id, field });
        case 'enum':
            return generateEnumField({ context, faker, field });
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field);
    }
}
function generateScalarField({ faker, field }) {
    if (field.seed.type !== 'default') {
        if (field.type === 'number') {
            return Number(generateFromSeedConfig(faker, field.seed));
        }
        else {
            return generateFromSeedConfig(faker, field.seed);
        }
    }
    switch (field.databaseType) {
        case 'String':
            return faker.lorem.words(3);
        case 'Int':
            return faker.number.int({ min: 1, max: 1000 });
        case 'BigInt':
            return faker.number.int({ min: 1, max: 100000 });
        case 'Boolean':
            return faker.datatype.boolean();
        case 'DateTime':
            return faker.date.between({ from: '2024-01-01T00:00:00.000Z', to: '2025-02-01T00:00:00.000Z' });
        case 'Decimal':
            return faker.number.float({ min: 1, max: 100000, multipleOf: 0.01 });
        case 'Float':
            return faker.number.float({ min: 0, max: 1 });
        case 'Json':
            throw new Error('Json type is not supported for seed data');
        case 'Bytes':
            throw new Error('Bytes type is not supported for seed data');
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field.databaseType);
    }
}
function generateRelationField({ faker, modelIds, model, id, field, }) {
    const relatedModelIds = modelIds.get(field.referencedModelName);
    if (model.name === field.referencedModelName && relatedModelIds.length === 1) {
        if (!field.isRequired) {
            return undefined;
        }
        throw new Error(`Self-referencing model ${model.name} must have at least 2 seed data entries!`);
    }
    if (relatedModelIds.length === 0) {
        if (!field.isRequired) {
            return undefined;
        }
        throw new Error(`Model ${field.referencedModelName} is not seeded but required for field ${field.modelName}.${field.name}!`);
    }
    while (true) {
        const referencedId = faker.helpers.arrayElement(relatedModelIds);
        if (referencedId !== id) {
            return referencedId;
        }
    }
}
function generateEnumField({ context, faker, field, }) {
    const enum_ = context.enums.get(field.enumName);
    const values = Array.from(enum_.members.values()).map((m) => m.value);
    return faker.helpers.arrayElement(values);
}
function generateDiscriminatedUnionField({ context, faker, modelIds, model, id, field, }) {
    const discriminantValues = Array.from(field.members.keys());
    const member = faker.helpers.arrayElement(discriminantValues);
    const result = {
        [field.discriminatorFieldName]: member,
    };
    for (const f of field.commonFields.values()) {
        result[f.name] = generateSeedDataFieldNative({ context, faker, modelIds, model, id, field: f });
    }
    for (const f of field.members.get(member).fields.values()) {
        result[f.name] = generateSeedDataFieldNative({ context, faker, modelIds, model, id, field: f });
    }
    return result;
}
function generateModelSeedDataJson({ model }) {
    return JSON.stringify(model.seedData.data, null, 2);
}
function generateModelSeedDataTypescript({ model }) {
    const splitNames = model.seedData.dataVariable.location.split('/');
    const jsonLocation = splitNames[splitNames.length - 1];
    return /* ts */ `
  import * as data from './${jsonLocation}.json'

  export const ${model.seedData.dataVariable.name} = (data as any).default`;
}
//# sourceMappingURL=model-seed-data.generator.js.map