import {Provider} from '@nestjs/common';
import {defer, lastValueFrom} from 'rxjs';

import {getEntity} from './orm/utils/decorator.utils';
import {RepositoryFactory} from './orm/repositories/repository.factory';
import {loadModel, Repository, ConnectionOptions, Connection} from './orm';
import {getModelToken, getConnectionToken, getRepositoryToken} from './utils/orm.utils';

export function createScyllaProviders(entities?: Function[], conn?: Connection | ConnectionOptions | string) {
    const providers: Provider[] = [];
    const providerModel = (entity) => ({
        inject: [getConnectionToken(conn)],
        provide: getModelToken(entity),
        useFactory: (connection: Connection) => {
            return lastValueFrom(defer(() => loadModel(connection, entity)));
        },
    });
    const provideRepository = (entity) => ({
        inject: [getModelToken(entity)],
        provide: getRepositoryToken(entity),
        useFactory: async (model) => RepositoryFactory.create(entity, model),
    });
    const provideCustomRepository = (EntityRepository) => {
        const entity = getEntity(EntityRepository);
        return {
            provide: getRepositoryToken(EntityRepository),
            useFactory: async (model) => RepositoryFactory.create(entity, model, EntityRepository),
            inject: [getModelToken(entity)],
        };
    };
    (entities || []).forEach((entity) => {
        if (entity.prototype instanceof Repository) {
            return providers.push(provideCustomRepository(entity));
        }
        return providers.push(providerModel(entity), provideRepository(entity));
    });
    return [...providers];
}
