"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applySnapshotsDiff = void 0;
const sqlgenerator_1 = require("./sqlgenerator");
const jsonDiffer_1 = require("./jsonDiffer");
const yup_1 = require("yup");
const jsonStatements_1 = require("./jsonStatements");
const columnSchema = yup_1.object({
    name: yup_1.string().required(),
    type: yup_1.string().required(),
    primaryKey: yup_1.boolean().optional(),
    unique: yup_1.boolean().optional(),
    defaultValue: yup_1.string().optional(),
    notNull: yup_1.boolean().optional(),
    references: yup_1.object({
        table: yup_1.string().required(),
        column: yup_1.string().required(),
        foreignKeyName: yup_1.string().required(),
        onDelete: yup_1.string().optional(),
        onUpdate: yup_1.string().optional(),
    }).optional()
}).noUnknown();
const applySnapshotsDiff = async (json1, json2, tablesResolver, columnsResolver) => {
    const diffResult = jsonDiffer_1.applyJsonDiff(json1, json2);
    const typedResult = diffResult;
    typedResult.addedTables.forEach(t => {
        Object.values(t.columns).forEach(column => {
            columnSchema.validate(column, { strict: true });
        });
    });
    typedResult.deletedTables.forEach(t => {
        Object.values(t.columns).forEach(column => {
            columnSchema.validate(column, { strict: true });
        });
    });
    typedResult.alteredTablesWithColumns.forEach(t => {
        t.added.forEach(column => {
            columnSchema.validate(column, { strict: true });
        });
        t.deleted.forEach(column => {
            columnSchema.validate(column, { strict: true });
        });
    });
    const { created, deleted, renamed } = await tablesResolver({ created: typedResult.addedTables, deleted: typedResult.deletedTables });
    const jsonStatements = [];
    const jsonCreateTables = created.map(it => {
        return jsonStatements_1.prepareCreateTableJson(it);
    });
    const jsonCreateIndexesForCreatedTables = created.map(it => {
        return jsonStatements_1.prepareCreateIndexesJson(it.name, it.indexes);
    }).flat();
    const jsonDropTables = deleted.map(it => {
        return jsonStatements_1.prepareDropTableJson(it);
    });
    const jsonRenameTables = renamed.map(it => {
        return jsonStatements_1.prepareRenameTableJson(it.from, it.to);
    });
    const renamedWithAlternations = jsonDiffer_1.diffForRenamedTables(renamed);
    const allAltered = typedResult.alteredTablesWithColumns.concat(renamedWithAlternations);
    const jsonRenameColumnsStatements = [];
    const allAlteredResolved = [];
    for (const table of allAltered) {
        const result = await columnsResolver({ tableName: table.name, created: table.added, deleted: table.deleted });
        // prepare oldTable and newTable
        jsonRenameColumnsStatements.push(...jsonStatements_1.prepareRenameColumns(table.name, result.renamed));
        const renamedColumnsAltered = result.renamed.map(it => jsonDiffer_1.diffForRenamedColumn(it.from, it.to));
        const resolved = {
            name: table.name,
            deleted: result.deleted,
            added: result.created,
            altered: renamedColumnsAltered,
            addedIndexes: table.addedIndexes,
            deletedIndexes: table.deletedIndexes
        };
        allAlteredResolved.push(resolved);
    }
    const jsonAlterTables = allAlteredResolved.map(it => {
        return jsonStatements_1.prepareAlterTableColumnsJson(it.name, it.deleted, it.added, it.altered);
    }).flat();
    const jsonCreateIndexesForAllAlteredTables = allAltered.map(it => {
        return jsonStatements_1.prepareCreateIndexesJson(it.name, it.addedIndexes || {});
    }).flat();
    const jsonDropIndexesForAllAlteredTables = allAltered.map(it => {
        return jsonStatements_1.prepareDropIndexesJson(it.name, it.deletedIndexes || {});
    }).flat();
    const jsonCreateReferencesForCreatedTables = created.map(it => {
        return jsonStatements_1.prepareCreateReferencesJson(it.name, Object.values(it.columns));
    }).flat();
    const jsonCreateReferencesForAllAlteredTables = allAltered.map(it => {
        return jsonStatements_1.prepareCreateReferencesJson(it.name, it.added);
    }).flat();
    const jsonCreateReferences = jsonCreateReferencesForCreatedTables.concat(jsonCreateReferencesForAllAlteredTables);
    // // Enums:
    // // - создание енама ✅
    // // - переименование енама (пока не делаю)⏳
    // // - добавление вэлью к енаму ✅
    // // - ренейм вейлью у енама (пока не делаю, это надо запрашивать опять же через слай)⏳
    // // - удаление енама -> чекать не используется ли где-то енам и сначала ранить миграции и в самом конце удаление енама⏳
    // // - удаление вэлью из енама -> блок ❌
    // const enums = result.addedEnums.map(it => {
    //   return prepareCreateEnum(it.name, it.values)
    // })
    const createEnums = diffResult.addedEnums.map(it => {
        return jsonStatements_1.prepareCreateEnumJson(it.name, it.values);
    });
    //todo: block enum rename, enum value rename and enun deletion for now
    const jsonAlterEnumsWithAddedValues = diffResult.alteredEnums.map(it => {
        return jsonStatements_1.prepareAddValuesToEnumJson(it.name, it.addedValues);
    }).flat();
    jsonStatements.push(...createEnums);
    jsonStatements.push(...jsonAlterEnumsWithAddedValues);
    jsonStatements.push(...jsonCreateTables);
    jsonStatements.push(...jsonDropTables);
    jsonStatements.push(...jsonRenameTables);
    jsonStatements.push(...jsonRenameColumnsStatements);
    jsonStatements.push(...jsonAlterTables);
    jsonStatements.push(...jsonCreateReferences);
    jsonStatements.push(...jsonCreateIndexesForCreatedTables);
    jsonStatements.push(...jsonCreateIndexesForAllAlteredTables);
    jsonStatements.push(...jsonDropIndexesForAllAlteredTables);
    const sqlStatements = sqlgenerator_1.fromJson(jsonStatements);
    return sqlStatements.join('\n');
};
exports.applySnapshotsDiff = applySnapshotsDiff;
// explicitely ask if tables were renamed, if yes - add those to altered tables, otherwise - deleted
// double check if user wants to delete particular table and warn him on data loss
//# sourceMappingURL=snapshotsDiffer.js.map