/** Mock data generator for testing and seeding. */
import { faker } from '@faker-js/faker';
import { v5 as uuidv5 } from 'uuid';
import { getPropertyEnumOptions } from '../domain/property-mappers.js';
import { toPascalCase } from './string-utils.js';
const NAMESPACE = '01a94203-63ba-4c07-bb92-bb61cd2b8e41';
// i18n langId for Spanish (used by deterministic seeders).
const SPANISH_LANG_ID = '4470b5ab-9d57-4c9d-a68f-5bf8e32f543a';
function randomNumber(maxLength) {
    return Math.floor(Number('1' + '0'.repeat(maxLength - 1)) +
        Math.random() * Number('9' + '0'.repeat(maxLength - 1)));
}
function randomIntegerDigits(digits, avoidEndZero = false) {
    let n1 = 1;
    let n2 = 9;
    if (digits > 1) {
        const multiplier = 10 ** (digits - 1);
        n1 *= multiplier;
        n2 *= multiplier;
    }
    const value = Math.floor(n1 + Math.random() * n2);
    return value % 10 === 0 && avoidEndZero
        ? randomIntegerDigits(digits, avoidEndZero)
        : value;
}
function randomDecimalDigits(totalDigits, decimalDigits) {
    return Number.parseFloat(randomIntegerDigits(totalDigits - decimalDigits).toString() +
        '.' +
        randomIntegerDigits(decimalDigits, true).toString());
}
function mockByFieldNameMeaning(fieldType, fieldName) {
    switch (fieldName) {
        case 'image': {
            if (fieldType === 'varchar')
                return faker.image.url();
            break;
        }
        case 'name': {
            if (fieldType === 'varchar')
                return faker.commerce.productName();
            break;
        }
        case 'slug': {
            if (fieldType === 'varchar')
                return faker.lorem.slug();
            break;
        }
    }
}
function mockRandom(fieldType, fieldName, opts = {}) {
    const { checkFieldNameMeaning = false, decimalDigits, length, maxLength, scapeQuotes = false, totalDigits, } = opts;
    if (checkFieldNameMeaning) {
        const meaningMock = mockByFieldNameMeaning(fieldType, fieldName);
        if (meaningMock)
            return meaningMock;
    }
    switch (fieldType) {
        case 'array': {
            return '[]';
        }
        case 'bigint':
        case 'bigint.unsigned': {
            return randomNumber(maxLength ?? 20);
        }
        case 'blob':
        case 'blob.long':
        case 'blob.medium':
        case 'blob.tiny': {
            return 'data:image/png;base64,iVBORw0KGgo=';
        }
        case 'boolean': {
            return faker.datatype.boolean();
        }
        case 'char': {
            return faker.string.alphanumeric(length);
        }
        case 'datatype.uuid':
        case 'id': {
            if (length !== 36)
                return faker.string.alphanumeric(length);
            return faker.string.uuid();
        }
        case 'date': {
            return faker.date.recent().toISOString().slice(0, 10);
        }
        case 'decimal': {
            return totalDigits && decimalDigits
                ? randomDecimalDigits(totalDigits, decimalDigits)
                : randomDecimalDigits(5, 2);
        }
        case 'encrypted': {
            return faker.string.alphanumeric(50);
        }
        case 'float': {
            return maxLength ? randomNumber(maxLength) : faker.number.float();
        }
        case 'int':
        case 'int.unsigned': {
            return randomNumber(maxLength ?? 6);
        }
        case 'json':
        case 'jsonb': {
            return scapeQuotes
                ? '{ "foo" : "bar" }'.replaceAll('"', String.raw `\"`)
                : '{ "foo" : "bar" }';
        }
        case 'json.array': {
            return scapeQuotes
                ? '["foo", "bar"]'.replaceAll('"', String.raw `\"`)
                : '["foo", "bar"]';
        }
        case 'json.availableLangs': {
            return scapeQuotes
                ? `["${SPANISH_LANG_ID}"]`.replaceAll('"', String.raw `\"`)
                : `["${SPANISH_LANG_ID}"]`;
        }
        case 'password':
        case 'random.alphaNumeric':
        case 'varchar': {
            return faker.string.alphanumeric(maxLength);
        }
        case 'relationship': {
            return '[]';
        }
        case 'smallint':
        case 'smallint.unsigned': {
            return randomNumber(maxLength ?? 4);
        }
        case 'text': {
            return faker.lorem.paragraph();
        }
        case 'timestamp':
        case 'timestamp.recent': {
            return faker.date.recent().toISOString().replace('T', ' ').slice(0, 19);
        }
        case 'tinyint':
        case 'tinyint.unsigned': {
            return randomNumber(maxLength ?? 2);
        }
        default: {
            throw new Error(`Mock type "${fieldType}" not recognized`);
        }
    }
}
function mockFixed(fieldType, fieldName, opts = {}) {
    const { checkFieldNameMeaning = false, decimalDigits, length, maxLength, scapeQuotes = false, totalDigits, } = opts;
    if (checkFieldNameMeaning) {
        const meaningMock = mockByFieldNameMeaning(fieldType, fieldName);
        if (meaningMock)
            return meaningMock;
    }
    switch (fieldType) {
        case 'array': {
            return '[]';
        }
        case 'bigint':
        case 'bigint.unsigned': {
            return Number.parseInt('1'.repeat(maxLength ?? 20), 10);
        }
        case 'boolean': {
            return faker.datatype.boolean();
        }
        case 'char': {
            return '*'.repeat(length ?? 1);
        }
        case 'datatype.uuid':
        case 'id': {
            if ((length ?? 1) !== 36)
                return '*'.repeat(length ?? 1);
            return faker.string.uuid();
        }
        case 'date': {
            return '2022-01-01';
        }
        case 'decimal': {
            return totalDigits && decimalDigits
                ? Number.parseFloat('1'.repeat(totalDigits - decimalDigits) +
                    '.' +
                    '1'.repeat(decimalDigits))
                : Number.parseFloat('11111.11');
        }
        case 'encrypted': {
            return '*'.repeat(50);
        }
        case 'int':
        case 'int.unsigned': {
            return Number.parseInt('1'.repeat(maxLength ?? 6), 10);
        }
        case 'json':
        case 'jsonb': {
            return scapeQuotes
                ? '{ "foo" : "bar" }'.replaceAll('"', String.raw `\"`)
                : '{ "foo" : "bar" }';
        }
        case 'json.array': {
            return scapeQuotes
                ? '["foo", "bar"]'.replaceAll('"', String.raw `\"`)
                : '["foo", "bar"]';
        }
        case 'json.availableLangs': {
            return scapeQuotes
                ? `["${SPANISH_LANG_ID}"]`.replaceAll('"', String.raw `\"`)
                : `["${SPANISH_LANG_ID}"]`;
        }
        case 'password':
        case 'random.alphaNumeric':
        case 'varchar': {
            return '*'.repeat(maxLength ?? 1);
        }
        case 'random.float': {
            return maxLength ? randomNumber(maxLength) : faker.number.float();
        }
        case 'relationship': {
            return '[]';
        }
        case 'smallint':
        case 'smallint.unsigned': {
            return Number.parseInt('1'.repeat(maxLength ?? 4), 10);
        }
        case 'text': {
            return faker.lorem.paragraph();
        }
        case 'timestamp':
        case 'timestamp.recent': {
            return '2022-01-01 00:00:00';
        }
        case 'tinyint':
        case 'tinyint.unsigned': {
            return Number.parseInt('1'.repeat(maxLength ?? 2), 10);
        }
        default: {
            throw new Error(`Mock fixed type "${fieldType}" not recognized`);
        }
    }
}
/**
 * Generate mock data for a property. Used in templates via `it.mock.mocker(opts)`.
 */
export function mocker(opts = {}) {
    const { checkFieldNameMeaning = true, decimalDigits, hasUuidSeed = true, length, maxLength, minLength, property, scapeQuotes = true, schema, totalDigits, type = 'seed', uuidSeed = 'aurora', } = opts;
    // Fixed UUID mode
    if (type === 'fixedUuid')
        return uuidv5(uuidSeed, NAMESPACE);
    // Spanish langId for i18n
    if (property?.type === 'id' &&
        (length ?? property?.length) === 36 &&
        property.isI18n &&
        property.name === 'langId')
        return SPANISH_LANG_ID;
    // Deterministic UUID for id fields
    if (property?.type === 'id' &&
        hasUuidSeed &&
        (length ?? property?.length) === 36)
        return uuidv5(uuidSeed, NAMESPACE);
    // Enum handling
    if (property?.type === 'enum' && type === 'seed') {
        const enumOpts = getPropertyEnumOptions(property);
        return enumOpts?.length
            ? `${toPascalCase(schema?.boundedContextName ?? '')}${toPascalCase(schema?.moduleName ?? '')}${toPascalCase(property.name)}Enum.${enumOpts[Math.floor(Math.random() * enumOpts.length)]}`
            : null;
    }
    if (property?.type === 'enum') {
        const enumOpts = getPropertyEnumOptions(property);
        return enumOpts?.length
            ? `'${enumOpts[Math.floor(Math.random() * enumOpts.length)]}'`
            : null;
    }
    // Relationship
    if (property?.type === 'relationship')
        return '[]';
    // Resolve decimal parameters
    let propertyTotalDigits = 5;
    let propertyDecimalDigits = 3;
    if (property && Array.isArray(property.decimals)) {
        propertyTotalDigits = property.decimals[0];
        propertyDecimalDigits = property.decimals[1];
    }
    const mockOpts = {
        checkFieldNameMeaning,
        decimalDigits: decimalDigits ?? propertyDecimalDigits,
        length: length ?? property?.length,
        maxLength: maxLength ??
            (property?.maxLength && property.maxLength > 1
                ? property.maxLength - 1
                : property?.maxLength),
        minLength: minLength ?? property?.minLength,
        scapeQuotes,
        totalDigits: totalDigits ?? propertyTotalDigits,
    };
    const fieldType = property?.faker ?? property?.type ?? 'varchar';
    const fieldName = property?.name ?? '';
    if (type === 'fixedData') {
        return mockFixed(fieldType, fieldName, mockOpts);
    }
    return mockRandom(fieldType, fieldName, mockOpts);
}
