/// <reference types="./mssql-introspector.d.ts" />
import { DEFAULT_MIGRATION_LOCK_TABLE, DEFAULT_MIGRATION_TABLE, } from '../../migration/migrator.js';
import { freeze } from '../../util/object-utils.js';
export class MssqlIntrospector {
    #db;
    constructor(db) {
        this.#db = db;
    }
    async getSchemas() {
        return await this.#db.selectFrom('sys.schemas').select('name').execute();
    }
    async getTables(options = { withInternalKyselyTables: false }) {
        const rawColumns = await this.#db
            .selectFrom('sys.tables as tables')
            .leftJoin('sys.schemas as table_schemas', 'table_schemas.schema_id', 'tables.schema_id')
            .innerJoin('sys.columns as columns', 'columns.object_id', 'tables.object_id')
            .innerJoin('sys.types as types', 'types.user_type_id', 'columns.user_type_id')
            .leftJoin('sys.schemas as type_schemas', 'type_schemas.schema_id', 'types.schema_id')
            .leftJoin('sys.extended_properties as comments', (join) => join
            .onRef('comments.major_id', '=', 'tables.object_id')
            .onRef('comments.minor_id', '=', 'columns.column_id')
            .on('comments.name', '=', 'MS_Description'))
            .$if(!options.withInternalKyselyTables, (qb) => qb
            .where('tables.name', '!=', DEFAULT_MIGRATION_TABLE)
            .where('tables.name', '!=', DEFAULT_MIGRATION_LOCK_TABLE))
            .select([
            'tables.name as table_name',
            (eb) => eb
                .ref('tables.type')
                .$castTo()
                .as('table_type'),
            'table_schemas.name as table_schema_name',
            'columns.default_object_id as column_default_object_id',
            'columns.generated_always_type_desc as column_generated_always_type',
            'columns.is_computed as column_is_computed',
            'columns.is_identity as column_is_identity',
            'columns.is_nullable as column_is_nullable',
            'columns.is_rowguidcol as column_is_rowguidcol',
            'columns.name as column_name',
            'types.is_nullable as type_is_nullable',
            'types.name as type_name',
            'type_schemas.name as type_schema_name',
            'comments.value as column_comment',
        ])
            .unionAll(this.#db
            .selectFrom('sys.views as views')
            .leftJoin('sys.schemas as view_schemas', 'view_schemas.schema_id', 'views.schema_id')
            .innerJoin('sys.columns as columns', 'columns.object_id', 'views.object_id')
            .innerJoin('sys.types as types', 'types.user_type_id', 'columns.user_type_id')
            .leftJoin('sys.schemas as type_schemas', 'type_schemas.schema_id', 'types.schema_id')
            .leftJoin('sys.extended_properties as comments', (join) => join
            .onRef('comments.major_id', '=', 'views.object_id')
            .onRef('comments.minor_id', '=', 'columns.column_id')
            .on('comments.name', '=', 'MS_Description'))
            .select([
            'views.name as table_name',
            'views.type as table_type',
            'view_schemas.name as table_schema_name',
            'columns.default_object_id as column_default_object_id',
            'columns.generated_always_type_desc as column_generated_always_type',
            'columns.is_computed as column_is_computed',
            'columns.is_identity as column_is_identity',
            'columns.is_nullable as column_is_nullable',
            'columns.is_rowguidcol as column_is_rowguidcol',
            'columns.name as column_name',
            'types.is_nullable as type_is_nullable',
            'types.name as type_name',
            'type_schemas.name as type_schema_name',
            'comments.value as column_comment',
        ]))
            .orderBy('table_schema_name')
            .orderBy('table_name')
            .orderBy('column_name')
            .execute();
        const tableDictionary = {};
        for (const rawColumn of rawColumns) {
            const key = `${rawColumn.table_schema_name}.${rawColumn.table_name}`;
            const table = (tableDictionary[key] =
                tableDictionary[key] ||
                    freeze({
                        columns: [],
                        isView: rawColumn.table_type === 'V ',
                        name: rawColumn.table_name,
                        schema: rawColumn.table_schema_name ?? undefined,
                    }));
            table.columns.push(freeze({
                dataType: rawColumn.type_name,
                dataTypeSchema: rawColumn.type_schema_name ?? undefined,
                hasDefaultValue: rawColumn.column_default_object_id > 0 ||
                    rawColumn.column_generated_always_type !== 'NOT_APPLICABLE' ||
                    rawColumn.column_is_identity ||
                    rawColumn.column_is_computed ||
                    rawColumn.column_is_rowguidcol,
                isAutoIncrementing: rawColumn.column_is_identity,
                isNullable: rawColumn.column_is_nullable && rawColumn.type_is_nullable,
                name: rawColumn.column_name,
                comment: rawColumn.column_comment ?? undefined,
            }));
        }
        return Object.values(tableDictionary);
    }
    async getMetadata(options) {
        return {
            tables: await this.getTables(options),
        };
    }
}
