"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var SolidIntrospectService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SolidIntrospectService = exports.coreSubscriberClasses = void 0;
const string_helper_1 = require("../helpers/string.helper");
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const model_metadata_helper_service_1 = require("../helpers/model-metadata-helper.service");
const model_metadata_repository_1 = require("../repository/model-metadata.repository");
const typeorm_1 = require("@nestjs/typeorm");
const computed_field_provider_decorator_1 = require("../decorators/computed-field-provider.decorator");
const dashboard_question_data_provider_decorator_1 = require("../decorators/dashboard-question-data-provider.decorator");
const dashboard_selection_provider_decorator_1 = require("../decorators/dashboard-selection-provider.decorator");
const error_codes_provider_decorator_1 = require("../decorators/error-codes-provider.decorator");
const mail_provider_decorator_1 = require("../decorators/mail-provider.decorator");
const scheduled_job_provider_decorator_1 = require("../decorators/scheduled-job-provider.decorator");
const security_rule_config_provider_decorator_1 = require("../decorators/security-rule-config-provider.decorator");
const selection_provider_decorator_1 = require("../decorators/selection-provider.decorator");
const extension_user_creation_provider_decorator_1 = require("../decorators/extension-user-creation-provider.decorator");
const solid_database_module_decorator_1 = require("../decorators/solid-database-module.decorator");
const whatsapp_provider_decorator_1 = require("../decorators/whatsapp-provider.decorator");
const solid_registry_1 = require("../helpers/solid-registry");
const audit_subscriber_1 = require("../subscribers/audit.subscriber");
const computed_entity_field_subscriber_1 = require("../subscribers/computed-entity-field.subscriber");
const created_by_updated_by_subscriber_1 = require("../subscribers/created-by-updated-by.subscriber");
const soft_delete_aware_event_subscriber_1 = require("../subscribers/soft-delete-aware-event.subscriber");
const settings_provider_decorator_1 = require("../decorators/settings-provider.decorator");
const sms_provider_decorator_1 = require("../decorators/sms-provider.decorator");
const setting_service_1 = require("./setting.service");
exports.coreSubscriberClasses = [
    audit_subscriber_1.AuditSubscriber,
    computed_entity_field_subscriber_1.ComputedEntityFieldSubscriber,
    created_by_updated_by_subscriber_1.CreatedByUpdatedBySubscriber,
    soft_delete_aware_event_subscriber_1.SoftDeleteAwareEventSubscriber
];
let SolidIntrospectService = SolidIntrospectService_1 = class SolidIntrospectService {
    constructor(discoveryService, reflector, metadataScanner, solidRegistry, moduleRef, settingService, modelMetadataRepo, modelMetadataHelperService) {
        this.discoveryService = discoveryService;
        this.reflector = reflector;
        this.metadataScanner = metadataScanner;
        this.solidRegistry = solidRegistry;
        this.moduleRef = moduleRef;
        this.settingService = settingService;
        this.modelMetadataRepo = modelMetadataRepo;
        this.modelMetadataHelperService = modelMetadataHelperService;
        this.logger = new common_1.Logger(SolidIntrospectService_1.name);
    }
    async onApplicationBootstrap() {
        this.logger.debug('Introspecting the application for Solid metadata');
        const seeders = this.discoveryService.getProviders().filter((provider) => this.isSeeder(provider));
        seeders.forEach((seeder) => {
            this.solidRegistry.registerSeeder(seeder);
        });
        const errorCodeProviders = this.discoveryService.getProviders().filter((provider) => this.isErrorCodeProvider(provider));
        errorCodeProviders.forEach((errorCodeProvider) => {
            this.solidRegistry.registerErrorCodeProvider(errorCodeProvider);
        });
        const selectionProviders = this.discoveryService.getProviders().filter((provider) => this.isSelectionProvider(provider));
        selectionProviders.forEach((selectionProvider) => {
            this.solidRegistry.registerSelectionProvider(selectionProvider);
        });
        const settingsProviders = this.discoveryService.getProviders().filter((provider) => this.isSettingsProvider(provider));
        settingsProviders.forEach((settingsProvider) => {
            this.solidRegistry.registerSettingsProvider(settingsProvider);
        });
        const dashboardVariableSelectionProviders = this.discoveryService.getProviders().filter((provider) => this.isDashboardVariableSelectionProvider(provider));
        dashboardVariableSelectionProviders.forEach((dashboardSelectionProvider) => {
            this.solidRegistry.registerDashboardVariableSelectionProvider(dashboardSelectionProvider);
        });
        const dashboardQuestionDataProviders = this.discoveryService.getProviders().filter((provider) => this.isDashboardQuestionDataProvider(provider));
        dashboardQuestionDataProviders.forEach((provider) => {
            this.solidRegistry.registerDashboardQuestionDataProvider(provider);
        });
        const computedFieldProviders = this.discoveryService.getProviders().filter((provider) => this.isComputedFieldProvider(provider));
        computedFieldProviders.forEach((computedFieldProvider) => {
            this.solidRegistry.registerComputedFieldProvider(computedFieldProvider);
        });
        const solidDatabaseModules = this.discoveryService.getProviders().filter((provider) => this.isSolidDatabaseModule(provider));
        solidDatabaseModules.forEach((solidDatabaseModule) => {
            this.solidRegistry.registerSolidDatabaseModule(solidDatabaseModule);
        });
        const allControllers = this.discoveryService.getControllers().map((controller) => {
            const { instance } = controller;
            return {
                name: controller.name,
                methods: this.metadataScanner.getAllMethodNames(Object.getPrototypeOf(instance))
            };
        });
        this.solidRegistry.registerControllers(new Set(allControllers));
        const allModules = this.discoveryService.getProviders().filter((provider) => this.isModule(provider));
        this.solidRegistry.registerModules(allModules);
        const scheduledJobProviders = this.discoveryService.getProviders().filter((provider) => this.isScheduledJobProvider(provider));
        scheduledJobProviders.forEach((scheduledJobProvider) => {
            this.solidRegistry.registerScheduledJobProvider(scheduledJobProvider);
        });
        const mailProviders = this.discoveryService.getProviders().filter((provider) => this.isMailProvider(provider));
        mailProviders.forEach((mailProvider) => {
            this.solidRegistry.registerMailProvider(mailProvider);
        });
        const whatsappProviders = this.discoveryService.getProviders().filter((provider) => this.isWhatsappProvider(provider));
        whatsappProviders.forEach((whatsappProvider) => {
            this.solidRegistry.registerWhatsappProvider(whatsappProvider);
        });
        const smsProviders = this.discoveryService.getProviders().filter((provider) => this.isSmsProvider(provider));
        smsProviders.forEach((smsProvider) => {
            this.solidRegistry.registerSmsProvider(smsProvider);
        });
        const securityRuleConfigProviders = this.discoveryService.getProviders().filter((provider) => this.isSecurityRuleConfigProvider(provider));
        securityRuleConfigProviders.forEach((securityRuleConfigProvider) => {
            this.solidRegistry.registerSecurityRuleConfigProvider(securityRuleConfigProvider);
        });
        const extensionUserCreationProviders = this.discoveryService.getProviders().filter((provider) => this.isExtensionUserCreationProvider(provider));
        extensionUserCreationProviders.forEach((provider) => {
            this.solidRegistry.registerExtensionUserCreationProvider(provider);
        });
        await this.bootstrapCoreTypeOrmSubscribers(solidDatabaseModules);
        await this.cacheAuditableModels();
        await this.settingService.updateSettingsCache();
    }
    async cacheAuditableModels() {
        const models = await this.modelMetadataRepo.find({
            where: { enableAuditTracking: true },
            relations: { fields: true, module: true },
        });
        const auditableSet = new Set();
        for (const model of models) {
            const allFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName);
            const hasAuditableField = allFields.some(field => field.enableAuditTracking &&
                !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
                !(field.type === 'relation' && field.relationType === 'one-to-many'));
            if (hasAuditableField) {
                auditableSet.add(model.singularName.toLowerCase());
            }
        }
        this.solidRegistry.registerAuditableModels(auditableSet);
        this.logger.debug(`Cached ${auditableSet.size} auditable model(s): ${[...auditableSet].join(', ')}`);
    }
    async bootstrapCoreTypeOrmSubscribers(dbModules) {
        for (const wrapper of dbModules) {
            const instance = wrapper.instance;
            if (!instance || typeof instance.name !== 'function') {
                this.logger.warn('Skipping a solid DB module wrapper with no instance or name() method');
                continue;
            }
            const dsName = instance.name();
            const token = dsName ? (0, typeorm_1.getDataSourceToken)(dsName) : (0, typeorm_1.getDataSourceToken)();
            let ds;
            try {
                ds = this.moduleRef.get(token, { strict: false });
            }
            catch (err) {
                this.logger.warn(`DataSource token for "${dsName ?? 'default'}" not found: ${err?.message ?? err}`);
            }
            if (!ds) {
                this.logger.warn(`No DataSource found for module "${dsName}". Skipping subscriber registration.`);
                continue;
            }
            if (!ds.isInitialized) {
                try {
                    await ds.initialize();
                }
                catch (err) {
                    this.logger.error(`Failed to initialize DataSource "${dsName}": ${err}`);
                    continue;
                }
            }
            for (const SubClass of exports.coreSubscriberClasses) {
                const alreadyRegistered = ds.subscribers.some((s) => s.constructor?.name === SubClass.name);
                if (alreadyRegistered) {
                    this.logger.debug(`Subscriber ${SubClass.name} already registered on datasource ${dsName ?? 'default'}`);
                    continue;
                }
                const subscriberInstance = await this.moduleRef.resolve(SubClass, undefined, { strict: false });
                subscriberInstance.bindToDataSource(ds);
            }
        }
    }
    isDashboardQuestionDataProvider(providerWrapper) {
        const { instance } = providerWrapper;
        if (!instance)
            return false;
        const provider = this.reflector.get(dashboard_question_data_provider_decorator_1.IS_DASHBOARD_QUESTION_DATA_PROVIDER, instance.constructor);
        return !!provider;
    }
    isSeeder(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const seedMethod = this.metadataScanner.getAllMethodNames(Object.getPrototypeOf(instance)).find((methodName) => methodName === 'seed');
        if (!seedMethod)
            return false;
        return true;
    }
    isErrorCodeProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isErrorCodeProvider = this.reflector.get(error_codes_provider_decorator_1.IS_ERROR_CODE_PROVIDER, instance.constructor);
        return !!isErrorCodeProvider;
    }
    isSelectionProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isSelectionProvider = this.reflector.get(selection_provider_decorator_1.IS_SELECTION_PROVIDER, instance.constructor);
        return !!isSelectionProvider;
    }
    isExtensionUserCreationProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        return !!this.reflector.get(extension_user_creation_provider_decorator_1.IS_EXTENSION_USER_CREATION_PROVIDER, instance.constructor);
    }
    isSettingsProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isSettingsProvider = this.reflector.get(settings_provider_decorator_1.IS_SETTINGS_PROVIDER, instance.constructor);
        return !!isSettingsProvider;
    }
    isDashboardVariableSelectionProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isDashboardSelectionProvider = this.reflector.get(dashboard_selection_provider_decorator_1.IS_DASHBOARD_VARIABLE_SELECTION_PROVIDER, instance.constructor);
        return !!isDashboardSelectionProvider;
    }
    isComputedFieldProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isComputedFieldProvider = this.reflector.get(computed_field_provider_decorator_1.IS_COMPUTED_FIELD_PROVIDER, instance.constructor);
        return !!isComputedFieldProvider;
    }
    isScheduledJobProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isScheduledJobProvider = this.reflector.get(scheduled_job_provider_decorator_1.IS_SCHEDULED_JOB_PROVIDER, instance.constructor);
        return !!isScheduledJobProvider;
    }
    isSolidDatabaseModule(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isSolidDatabaseModule = this.reflector.get(solid_database_module_decorator_1.IS_SOLID_DATABASE_MODULE, instance.constructor);
        return !!isSolidDatabaseModule;
    }
    isMailProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isMailProvider = this.reflector.get(mail_provider_decorator_1.IS_MAIL_PROVIDER, instance.constructor);
        return !!isMailProvider;
    }
    isWhatsappProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isWhatsappProvider = this.reflector.get(whatsapp_provider_decorator_1.IS_WA_PROVIDER, instance.constructor);
        return !!isWhatsappProvider;
    }
    isSmsProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isSmsProvider = this.reflector.get(sms_provider_decorator_1.IS_SMS_PROVIDER, instance.constructor);
        return !!isSmsProvider;
    }
    isSecurityRuleConfigProvider(provider) {
        const { instance } = provider;
        if (!instance)
            return false;
        const isSecurityRuleConfigProvider = this.reflector.get(security_rule_config_provider_decorator_1.IS_SECURITY_RULE_CONFIG_PROVIDER, instance.constructor);
        return !!isSecurityRuleConfigProvider;
    }
    isModule(provider) {
        const metatype = provider.metatype;
        if (metatype && typeof metatype === 'function' && Reflect.getMetadata('imports', metatype)) {
            return true;
        }
        if (provider.instance && typeof provider.instance === 'object') {
            if ('module' in provider.instance && typeof provider.instance.module === 'function') {
                return true;
            }
        }
        return false;
    }
    getCRUDService(modelSingularName) {
        const provider = this.getProvider(`${(0, string_helper_1.classify)(modelSingularName)}Service`);
        return provider?.instance;
    }
    getProvider(providerName) {
        return this.discoveryService.getProviders().find((provider) => provider.name === providerName);
    }
};
exports.SolidIntrospectService = SolidIntrospectService;
exports.SolidIntrospectService = SolidIntrospectService = SolidIntrospectService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [core_1.DiscoveryService,
        core_1.Reflector,
        core_1.MetadataScanner,
        solid_registry_1.SolidRegistry,
        core_1.ModuleRef,
        setting_service_1.SettingService,
        model_metadata_repository_1.ModelMetadataRepository,
        model_metadata_helper_service_1.ModelMetadataHelperService])
], SolidIntrospectService);
//# sourceMappingURL=solid-introspect.service.js.map