/**
 * MongoDB Platform Service Provider
 */
import { BasePlatformServiceProvider } from './base.js';
import * as plugins from '../../../plugins.js';
import { logger } from '../../../logging.js';
import { getErrorMessage } from '../../../utils/error.js';
import { credentialEncryption } from '../../encryption.js';
export class MongoDBProvider extends BasePlatformServiceProvider {
    type = 'mongodb';
    displayName = 'MongoDB';
    resourceTypes = ['database'];
    constructor(oneboxRef) {
        super(oneboxRef);
    }
    async getDefaultConfig() {
        return {
            image: 'mongo:4.4',
            port: 27017,
            volumes: [`${await this.getPlatformDataDir('mongodb')}:/data/db`],
            environment: {
                MONGO_INITDB_ROOT_USERNAME: 'admin',
                // Password will be generated and stored encrypted
            },
        };
    }
    getEnvVarMappings() {
        return [
            { envVar: 'MONGODB_URI', credentialPath: 'connectionString' },
            { envVar: 'MONGODB_HOST', credentialPath: 'host' },
            { envVar: 'MONGODB_PORT', credentialPath: 'port' },
            { envVar: 'MONGODB_DATABASE', credentialPath: 'database' },
            { envVar: 'MONGODB_USERNAME', credentialPath: 'username' },
            { envVar: 'MONGODB_PASSWORD', credentialPath: 'password' },
        ];
    }
    async deployContainer() {
        const config = await this.getDefaultConfig();
        const containerName = this.getContainerName();
        const dataDir = await this.getPlatformDataDir('mongodb');
        logger.info(`Deploying MongoDB platform service as ${containerName}...`);
        // Check if we have existing data and stored credentials
        const platformService = await this.oneboxRef.database.getPlatformServiceByType(this.type);
        let adminCredentials;
        let dataExists = false;
        // Check if data directory has existing MongoDB data
        try {
            const stat = await plugins.fs.stat(`${dataDir}/WiredTiger`);
            dataExists = stat.isFile;
            logger.info(`MongoDB data directory exists with WiredTiger file`);
        }
        catch {
            // WiredTiger file doesn't exist, this is a fresh install
            dataExists = false;
        }
        if (platformService?.adminCredentialsEncrypted) {
            // Reuse existing credentials from database
            logger.info('Reusing existing MongoDB credentials (data directory already initialized)');
            adminCredentials = await credentialEncryption.decrypt(platformService.adminCredentialsEncrypted);
        }
        else {
            // Generate new credentials for fresh deployment
            logger.info('Generating new MongoDB admin credentials');
            adminCredentials = {
                username: 'admin',
                password: credentialEncryption.generatePassword(32),
            };
            // If data exists but we don't have credentials, we need to wipe the data
            if (dataExists) {
                logger.warn('MongoDB data exists but no credentials in database - wiping data directory');
                try {
                    await plugins.fs.remove(dataDir, { recursive: true });
                }
                catch (e) {
                    logger.error(`Failed to wipe MongoDB data directory: ${getErrorMessage(e)}`);
                    throw new Error('Cannot deploy MongoDB: data directory exists without credentials');
                }
            }
        }
        // Ensure data directory exists
        try {
            await plugins.fs.mkdir(dataDir, { recursive: true });
        }
        catch (e) {
            // Directory might already exist
            if (!plugins.isAlreadyExistsError(e)) {
                logger.warn(`Could not create MongoDB data directory: ${getErrorMessage(e)}`);
            }
        }
        // Create container using Docker API
        const envVars = [
            `MONGO_INITDB_ROOT_USERNAME=${adminCredentials.username}`,
            `MONGO_INITDB_ROOT_PASSWORD=${adminCredentials.password}`,
        ];
        // Use Docker to create the container
        const containerId = await this.oneboxRef.docker.createPlatformContainer({
            name: containerName,
            image: config.image,
            port: config.port,
            env: envVars,
            volumes: config.volumes,
            volumeMounts: [await this.getPlatformDataVolume('/data/db', 'mongodb')],
            network: this.getNetworkName(),
        });
        // Store encrypted admin credentials (only update if new or changed)
        const encryptedCreds = await credentialEncryption.encrypt(adminCredentials);
        if (platformService) {
            await this.oneboxRef.database.updatePlatformService(platformService.id, {
                containerId,
                adminCredentialsEncrypted: encryptedCreds,
                status: 'starting',
            });
        }
        logger.success(`MongoDB container created: ${containerId}`);
        return containerId;
    }
    async stopContainer(containerId) {
        logger.info(`Stopping MongoDB container ${containerId}...`);
        await this.oneboxRef.docker.stopContainer(containerId);
        logger.success('MongoDB container stopped');
    }
    async healthCheck() {
        try {
            logger.info('MongoDB health check: starting...');
            const platformService = await this.oneboxRef.database.getPlatformServiceByType(this.type);
            if (!platformService) {
                logger.info('MongoDB health check: platform service not found in database');
                return false;
            }
            if (!platformService.adminCredentialsEncrypted) {
                logger.info('MongoDB health check: no admin credentials stored');
                return false;
            }
            if (!platformService.containerId) {
                logger.info('MongoDB health check: no container ID in database record');
                return false;
            }
            logger.info(`MongoDB health check: using container ID ${platformService.containerId.substring(0, 12)}...`);
            const adminCreds = await credentialEncryption.decrypt(platformService.adminCredentialsEncrypted);
            // Use docker exec to run health check inside the container
            // This avoids network issues with overlay networks
            const result = await this.oneboxRef.docker.execInContainer(platformService.containerId, ['mongo', '--eval', 'db.adminCommand("ping")', '--username', adminCreds.username, '--password', adminCreds.password, '--authenticationDatabase', 'admin', '--quiet']);
            if (result.exitCode === 0) {
                logger.info('MongoDB health check: success');
                return true;
            }
            else {
                logger.info(`MongoDB health check failed: exit code ${result.exitCode}, stderr: ${result.stderr.substring(0, 200)}`);
                return false;
            }
        }
        catch (error) {
            logger.info(`MongoDB health check exception: ${getErrorMessage(error)}`);
            return false;
        }
    }
    async provisionResource(userService) {
        const platformService = await this.oneboxRef.database.getPlatformServiceByType(this.type);
        if (!platformService || !platformService.adminCredentialsEncrypted || !platformService.containerId) {
            throw new Error('MongoDB platform service not found or not configured');
        }
        const adminCreds = await credentialEncryption.decrypt(platformService.adminCredentialsEncrypted);
        const containerName = this.getContainerName();
        // Generate resource names and credentials
        const dbName = this.generateResourceName(userService.name);
        const username = this.generateResourceName(userService.name);
        const password = credentialEncryption.generatePassword(32);
        logger.info(`Provisioning MongoDB database '${dbName}' for service '${userService.name}'...`);
        // Use docker exec to provision inside the container (avoids host port mapping issues)
        const escapedPassword = password.replace(/'/g, "'\\''");
        const escapedAdminPassword = adminCreds.password.replace(/'/g, "'\\''");
        // Create database and user via mongo inside the container
        const mongoScript = `
      db = db.getSiblingDB('${dbName}');
      db.createCollection('_onebox_init');
      db.createUser({
        user: '${username}',
        pwd: '${escapedPassword}',
        roles: [{ role: 'readWrite', db: '${dbName}' }]
      });
      print('PROVISION_SUCCESS');
    `;
        const result = await this.oneboxRef.docker.execInContainer(platformService.containerId, [
            'mongo',
            '--username', adminCreds.username,
            '--password', escapedAdminPassword,
            '--authenticationDatabase', 'admin',
            '--quiet',
            '--eval', mongoScript,
        ]);
        if (result.exitCode !== 0 || !result.stdout.includes('PROVISION_SUCCESS')) {
            throw new Error(`Failed to provision MongoDB database: exit code ${result.exitCode}, output: ${result.stdout.substring(0, 200)} ${result.stderr.substring(0, 200)}`);
        }
        logger.success(`MongoDB database '${dbName}' provisioned with user '${username}'`);
        // Build the credentials and env vars
        const credentials = {
            host: containerName,
            port: '27017',
            database: dbName,
            username,
            password,
            connectionString: `mongodb://${username}:${password}@${containerName}:27017/${dbName}?authSource=${dbName}`,
        };
        // Map credentials to env vars
        const envVars = {};
        for (const mapping of this.getEnvVarMappings()) {
            if (credentials[mapping.credentialPath]) {
                envVars[mapping.envVar] = credentials[mapping.credentialPath];
            }
        }
        return {
            type: 'database',
            name: dbName,
            credentials,
            envVars,
        };
    }
    async deprovisionResource(resource, credentials) {
        const platformService = await this.oneboxRef.database.getPlatformServiceByType(this.type);
        if (!platformService || !platformService.adminCredentialsEncrypted || !platformService.containerId) {
            throw new Error('MongoDB platform service not found or not configured');
        }
        const adminCreds = await credentialEncryption.decrypt(platformService.adminCredentialsEncrypted);
        const escapedAdminPassword = adminCreds.password.replace(/'/g, "'\\''");
        logger.info(`Deprovisioning MongoDB database '${resource.resourceName}'...`);
        const mongoScript = `
      db = db.getSiblingDB('${resource.resourceName}');
      try { db.dropUser('${credentials.username}'); } catch(e) { print('User drop failed: ' + e); }
      db.dropDatabase();
      print('DEPROVISION_SUCCESS');
    `;
        const result = await this.oneboxRef.docker.execInContainer(platformService.containerId, [
            'mongo',
            '--username', adminCreds.username,
            '--password', escapedAdminPassword,
            '--authenticationDatabase', 'admin',
            '--quiet',
            '--eval', mongoScript,
        ]);
        if (result.exitCode !== 0) {
            logger.warn(`MongoDB deprovision returned exit code ${result.exitCode}: ${result.stderr.substring(0, 200)}`);
        }
        logger.success(`MongoDB database '${resource.resourceName}' dropped`);
    }
}
//# sourceMappingURL=data:application/json;base64, 