// METADATA // {"ai-commented":{"service":"claude"}}
/*
 * Copyright (C) 2024-present Puter Technologies Inc.
 *
 * This file is part of Puter.
 *
 * Puter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
const { AdvancedBase } = require('@heyputer/putility');
const Library = require('./definitions/Library');
const { NotificationES } = require('./om/entitystorage/NotificationES');
const { ProtectedAppES } = require('./om/entitystorage/ProtectedAppES');
const { Context } = require('./util/context');
const { LLOWrite } = require('./filesystem/ll_operations/ll_write');
const { LLRead } = require('./filesystem/ll_operations/ll_read');
const { RuntimeModule } = require('./extension/RuntimeModule.js');
const { TYPE_DIRECTORY, TYPE_FILE } = require('./filesystem/FSNodeContext.js');
const { TDetachable } = require('@heyputer/putility/src/traits/traits.js');
const { MultiDetachable } = require('@heyputer/putility/src/libs/listener.js');
const { OperationFrame } = require('./services/OperationTraceService');

/**
 * @footgun - real install method is defined above
 */
const install = async ({ context, services, app, useapi, modapi }) => {
    const config = require('./config');

    // === LIBRARIES ===

    useapi.withuse(() => {
        def('Service', require('./services/BaseService'));
        def('Module', AdvancedBase);
        def('Library', Library);

        def('core.util.helpers', require('./helpers'));
        def('core.util.permission', require('./services/auth/permissionUtils.mjs').PermissionUtil);
        def('puter.middlewares.auth', require('./middleware/auth2'));
        def('puter.middlewares.configurable_auth', require('./middleware/configurable_auth'));
        def('puter.middlewares.anticsrf', require('./middleware/anticsrf'));

        def('core.APIError', require('./api/APIError'));
        def('core.Context', Context);

        def('core', require('./services/auth/Actor'), { assign: true });
        def('core', {
            TDetachable,
            MultiDetachable,
        }, { assign: true });
        def('core.config', config);

        // Note: this is an incomplete export; it was added for a proprietary
        // extension. Contributors may wish to add definitions in the 'fs.'
        // scope. Needing to add these individually is possibly a symptom of an
        // anti-pattern; "export filesystem operations to extensions" is one
        // statement in English, so maybe it should be one statement of code.
        def('core.fs', {
            LLOWrite,
            LLRead,
            TYPE_DIRECTORY,
            TYPE_FILE,
            OperationFrame,
        });
        def('core.fs.selectors', require('./filesystem/node/selectors'));
        def('core.util.stream', require('./util/streamutil'));
        def('web', require('./util/expressutil'));
        def('core.validation', require('@heyputer/backend-core-0').validation);

        def('core.database', require('./services/database/consts.js'));

        // Add otelutil functions to `core.`
        def('core.spanify', require('./util/otelutil').spanify);
        def('core.abtest', require('./util/otelutil').abtest);

        // Extension compatibility
        const runtimeModule = new RuntimeModule({ name: 'core' });
        context.get('runtime-modules').register(runtimeModule);
        runtimeModule.exports = useapi.use('core');
    });

    useapi.withuse(() => {
        const ArrayUtil = require('./libraries/ArrayUtil');
        services.registerService('util-array', ArrayUtil);

        const LibTypeTagged = require('./libraries/LibTypeTagged');
        services.registerService('lib-type-tagged', LibTypeTagged);
    });

    modapi.libdir('core.util', './util');

    // === SERVICES ===

    // /!\ IMPORTANT /!\
    // For new services, put the import immediately above the
    // call to services.registerService. We'll clean this up
    // in a future PR.

    const { CommandService } = require('./services/CommandService');
    const { HTTPThumbnailService } = require('./services/thumbnails/HTTPThumbnailService');
    const { PureJSThumbnailService } = require('./services/thumbnails/PureJSThumbnailService');
    const { NAPIThumbnailService } = require('./services/thumbnails/NAPIThumbnailService');
    const { RateLimitService } = require('./services/sla/RateLimitService');
    const { AuthService } = require('./services/auth/AuthService');
    const { SLAService } = require('./services/sla/SLAService');
    const { PermissionService } = require('./services/auth/PermissionService');
    const { ACLService } = require('./services/auth/ACLService');
    const { CoercionService } = require('./services/drivers/CoercionService');
    const { PuterSiteService } = require('./services/PuterSiteService');
    const { ContextInitService } = require('./services/ContextInitService');
    const { IdentificationService } = require('./services/abuse-prevention/IdentificationService');
    const { AuthAuditService } = require('./services/abuse-prevention/AuthAuditService');
    const { RegistryService } = require('./services/RegistryService');
    const { RegistrantService } = require('./services/RegistrantService');
    const { SystemValidationService } = require('./services/SystemValidationService');
    const { EntityStoreService } = require('./services/EntityStoreService');
    const SQLES = require('./om/entitystorage/SQLES');
    const ValidationES = require('./om/entitystorage/ValidationES');
    const { SetOwnerES } = require('./om/entitystorage/SetOwnerES');
    const AppES = require('./om/entitystorage/AppES');
    const WriteByOwnerOnlyES = require('./om/entitystorage/WriteByOwnerOnlyES');
    const SubdomainES = require('./om/entitystorage/SubdomainES');
    const { MaxLimitES } = require('./om/entitystorage/MaxLimitES');
    const { AppLimitedES } = require('./om/entitystorage/AppLimitedES');
    const { ReadOnlyES } = require('./om/entitystorage/ReadOnlyES');
    const { OwnerLimitedES } = require('./om/entitystorage/OwnerLimitedES');
    const { ESBuilder } = require('./om/entitystorage/ESBuilder');
    const { Eq, Or } = require('./om/query/query');
    const { MakeProdDebuggingLessAwfulService } = require('./services/MakeProdDebuggingLessAwfulService');
    const { ConfigurableCountingService } = require('./services/ConfigurableCountingService');
    const { FSLockService } = require('./services/fs/FSLockService');
    const { StrategizedService } = require('./services/StrategizedService');
    const FilesystemAPIService = require('./services/FilesystemAPIService');
    const ServeGUIService = require('./services/ServeGUIService');
    const PuterAPIService = require('./services/PuterAPIService');
    const { RefreshAssociationsService } = require('./services/RefreshAssociationsService');
    // Service names beginning with '__' aren't called by other services;
    // these provide data/functionality to other services or produce
    // side-effects from the events of other services.

    // === Services which extend BaseService ===
    services.registerService('system-validation', SystemValidationService);
    services.registerService('commands', CommandService);
    services.registerService('__api-filesystem', FilesystemAPIService);
    services.registerService('__api', PuterAPIService);
    services.registerService('__gui', ServeGUIService);
    services.registerService('registry', RegistryService);
    services.registerService('__registrant', RegistrantService);
    services.registerService('fslock', FSLockService);
    services.registerService('es:app', EntityStoreService, {
        entity: 'app',
        upstream: ESBuilder.create([
            SQLES, { table: 'app', debug: true },
            AppES,
            AppLimitedES, {
                permission_prefix: 'apps-of-user',
                // When apps query es:apps, they're allowed to see apps which
                // are approved for listing and they're allowed to see their
                // own entry.
                exception: async () => {
                    const actor = Context.get('actor');
                    return new Or({
                        children: [
                            new Eq({
                                key: 'approved_for_listing',
                                value: 1,
                            }),
                            new Eq({
                                key: 'uid',
                                value: actor.type.app.uid,
                            }),
                        ],
                    });
                },
            },
            WriteByOwnerOnlyES,
            ValidationES,
            SetOwnerES,
            ProtectedAppES,
            MaxLimitES, { max: 5000 },
        ]),
    });

    const { EntriService } = require('./services/EntriService.js');
    services.registerService('entri-service', EntriService);

    const { InformationService } = require('./services/information/InformationService');
    services.registerService('information', InformationService);

    const { TraceService } = require('./services/TraceService.js');
    services.registerService('traceService', TraceService);

    const { FilesystemService } = require('./filesystem/FilesystemService');
    services.registerService('filesystem', FilesystemService);

    services.registerService('es:subdomain', EntityStoreService, {
        entity: 'subdomain',
        upstream: ESBuilder.create([
            SQLES, { table: 'subdomains', debug: true },
            SubdomainES,
            AppLimitedES, { permission_prefix: 'subdomains-of-user' },
            WriteByOwnerOnlyES,
            ValidationES,
            SetOwnerES,
            MaxLimitES, { max: 5000 },
        ]),
    });
    services.registerService('es:notification', EntityStoreService, {
        entity: 'notification',
        upstream: ESBuilder.create([
            SQLES, { table: 'notification', debug: true },
            NotificationES,
            OwnerLimitedES,
            ReadOnlyES,
            SetOwnerES,
            MaxLimitES, { max: 200 },
        ]),
    });
    services.registerService('rate-limit', RateLimitService);
    services.registerService('auth', AuthService);
    // services.registerService('preauth', PreAuthService);
    services.registerService('permission', PermissionService);
    services.registerService('sla', SLAService);
    services.registerService('acl', ACLService);
    services.registerService('coercion', CoercionService);
    services.registerService('puter-site', PuterSiteService);
    services.registerService('context-init', ContextInitService);
    services.registerService('identification', IdentificationService);
    services.registerService('auth-audit', AuthAuditService);
    services.registerService('counting', ConfigurableCountingService);
    services.registerService('thumbnails', StrategizedService, {
        strategy_key: 'engine',
        default_strategy: 'purejs',
        strategies: {
            napi: [NAPIThumbnailService],
            purejs: [PureJSThumbnailService],
            http: [HTTPThumbnailService],
        },
    });
    services.registerService('__refresh-assocs', RefreshAssociationsService);
    services.registerService('__prod-debugging', MakeProdDebuggingLessAwfulService);
    if ( config.env == 'dev' && !config.no_devsocket ) {
        const { DevSocketService } = require('./services/DevSocketService.js');
        services.registerService('dev-socket', DevSocketService);
    }
    if ( (config.env == 'dev' && !config.no_devconsole && process.env.DEVCONSOLE) || config.devconsole ) {
        const { DevConsoleService } = require('./services/DevConsoleService');
        services.registerService('dev-console', DevConsoleService);
    } else {
        const { NullDevConsoleService } = require('./services/NullDevConsoleService');
        services.registerService('dev-console', NullDevConsoleService);
    }

    const { EventService } = require('./services/EventService');
    services.registerService('event', EventService);

    const { PuterVersionService } = require('./services/PuterVersionService');
    services.registerService('puter-version', PuterVersionService);

    const { SessionService } = require('./services/SessionService');
    services.registerService('session', SessionService);

    const { EdgeRateLimitService } = require('./services/abuse-prevention/EdgeRateLimitService');
    services.registerService('edge-rate-limit', EdgeRateLimitService);

    const { CleanEmailService } = require('./services/CleanEmailService');
    services.registerService('clean-email', CleanEmailService);

    const { Emailservice } = require('./services/EmailService');
    services.registerService('email', Emailservice);

    const { TokenService } = require('./services/auth/TokenService');
    services.registerService('token', TokenService);

    const { OTPService } = require('./services/auth/OTPService');
    services.registerService('otp', OTPService);

    const { UserProtectedEndpointsService } = require('./services/web/UserProtectedEndpointsService');
    services.registerService('__user-protected-endpoints', UserProtectedEndpointsService);

    const { AntiCSRFService } = require('./services/auth/AntiCSRFService');
    services.registerService('anti-csrf', AntiCSRFService);

    const { LockService } = require('./services/LockService');
    services.registerService('lock', LockService);

    const { PuterHomepageService } = require('./services/PuterHomepageService');
    services.registerService('puter-homepage', PuterHomepageService);

    const { GetUserService } = require('./services/GetUserService');
    services.registerService('get-user', GetUserService);

    const { DetailProviderService } = require('./services/DetailProviderService');
    services.registerService('whoami', DetailProviderService);

    const { DevTODService } = require('./services/DevTODService');
    services.registerService('__dev-tod', DevTODService);

    const { DriverService } = require('./services/drivers/DriverService');
    services.registerService('driver', DriverService);

    const { ScriptService } = require('./services/ScriptService');
    services.registerService('script', ScriptService);

    const { NotificationService } = require('./services/NotificationService');
    services.registerService('notification', NotificationService);

    const { ShareService } = require('./services/ShareService');
    services.registerService('share', ShareService);

    const { GroupService } = require('./services/auth/GroupService');
    services.registerService('group', GroupService);

    const { VirtualGroupService } = require('./services/auth/VirtualGroupService');
    services.registerService('virtual-group', VirtualGroupService);

    const { PermissionAPIService } = require('./services/PermissionAPIService');
    services.registerService('__permission-api', PermissionAPIService);

    const { AnomalyService } = require('./services/AnomalyService');
    services.registerService('anomaly', AnomalyService);

    const { HelloWorldService } = require('./services/HelloWorldService');
    services.registerService('hello-world', HelloWorldService);

    const { SystemDataService } = require('./services/SystemDataService');
    services.registerService('system-data', SystemDataService);

    const { SUService } = require('./services/SUService');
    services.registerService('su', SUService);

    const { ShutdownService } = require('./services/ShutdownService');
    services.registerService('shutdown', ShutdownService);

    const { BootScriptService } = require('./services/BootScriptService');
    services.registerService('boot-script', BootScriptService);

    const { FeatureFlagService } = require('./services/FeatureFlagService');
    services.registerService('feature-flag', FeatureFlagService);

    const { KernelInfoService } = require('./services/KernelInfoService');
    services.registerService('kernel-info', KernelInfoService);

    const { DriverUsagePolicyService } = require('./services/drivers/DriverUsagePolicyService');
    services.registerService('driver-usage-policy', DriverUsagePolicyService);

    const { CommentService } = require('./services/CommentService');
    services.registerService('comment', CommentService);

    const { ReferralCodeService } = require('./services/ReferralCodeService');
    services.registerService('referral-code', ReferralCodeService);

    const { VerifiedGroupService } = require('./services/VerifiedGroupService');
    services.registerService('__verified-group', VerifiedGroupService);

    const { UserService } = require('./services/UserService');
    services.registerService('user', UserService);

    const { WSPushService } = require('./services/WSPushService');
    services.registerService('__event-push-ws', WSPushService);

    const { SNSService } = require('./services/SNSService');
    services.registerService('sns', SNSService);

    const { PerformanceMonitor } = require('./monitor/PerformanceMonitor');
    services.registerService('performance-monitor', PerformanceMonitor);

    const { WispService } = require('./services/WispService');
    services.registerService('wisp', WispService);
    // const { AWSSecretsPopulator } = require('./services/AWSSecretsPopulator.js');
    // services.registerService('awsthing', AWSSecretsPopulator);
    const { WebDavFS } = require('./services/WebDAV/WebDAVService.js');
    services.registerService('dav', WebDavFS);

    const { RequestMeasureService } = require('./services/RequestMeasureService');
    services.registerService('request-measure', RequestMeasureService);

    const { ThreadService } = require('./services/ThreadService');
    services.registerService('thread', ThreadService);

    const { ChatAPIService } = require('./services/ChatAPIService');
    services.registerService('__chat-api', ChatAPIService);

    const { WorkerService } = require('./services/worker/WorkerService');
    services.registerService('worker-service', WorkerService);

    const { MeteringServiceWrapper } = require('./services/MeteringService/MeteringServiceWrapper.mjs');
    services.registerService('meteringService', MeteringServiceWrapper);

    const { PermissionShortcutService } = require('./services/auth/PermissionShortcutService');
    services.registerService('permission-shortcut', PermissionShortcutService);

    const { FileCacheService } = require('./services/file-cache/FileCacheService');
    services.registerService('file-cache', FileCacheService);
};

const install_legacy = async ({ services }) => {
    const { OperationTraceService } = require('./services/OperationTraceService');
    const { ClientOperationService } = require('./services/ClientOperationService');
    const { EngPortalService } = require('./services/EngPortalService');

    // === Services which do not yet extend BaseService ===
    // services.registerService('filesystem', FilesystemService);
    services.registerService('operationTrace', OperationTraceService);
    services.registerService('client-operation', ClientOperationService);
    services.registerService('engineering-portal', EngPortalService);

};

/**
 * Core module for the Puter platform that includes essential services including
 * authentication, filesystems, rate limiting, permissions, and various API endpoints.
 *
 * This is a monolithic module. Incrementally, services should be migrated to
 * Core2Module and other modules instead. Core2Module has a smaller scope, and each
 * new module will be a cohesive concern. Once CoreModule is empty, it will be removed
 * and Core2Module will take on its name.
 */
class CoreModule extends AdvancedBase {
    dirname () {
        return __dirname;
    }
    async install (context) {
        const services = context.get('services');
        const app = context.get('app');
        const useapi = context.get('useapi');
        const modapi = context.get('modapi');
        await install({ context, services, app, useapi, modapi });
    }

    /**
    * Installs legacy services that don't extend BaseService and require special handling.
    * These services were created before the BaseService class existed and don't listen
    * to the init event. They need to be installed after the init event is dispatched
    * due to initialization order dependencies.
    *
    * @param {Object} context - The context object containing service references
    * @param {Object} context.services - Service registry for registering legacy services
    * @returns {Promise<void>} Resolves when legacy services are installed
    */
    async install_legacy (context) {
        const services = context.get('services');
        await install_legacy({ services });
    }
}

module.exports = CoreModule;