/**
 * MariaDB 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 MariaDBProvider extends BasePlatformServiceProvider {
    type = 'mariadb';
    displayName = 'MariaDB';
    resourceTypes = ['database'];
    constructor(oneboxRef) {
        super(oneboxRef);
    }
    async getDefaultConfig() {
        return {
            image: 'mariadb:11',
            port: 3306,
            volumes: [`${await this.getPlatformDataDir('mariadb')}:/var/lib/mysql`],
            environment: {
                MARIADB_ROOT_PASSWORD: '',
                // Password will be generated and stored encrypted
            },
        };
    }
    getEnvVarMappings() {
        return [
            { envVar: 'MARIADB_HOST', credentialPath: 'host' },
            { envVar: 'MARIADB_PORT', credentialPath: 'port' },
            { envVar: 'MARIADB_DATABASE', credentialPath: 'database' },
            { envVar: 'MARIADB_USER', credentialPath: 'username' },
            { envVar: 'MARIADB_PASSWORD', credentialPath: 'password' },
            { envVar: 'MARIADB_URI', credentialPath: 'connectionString' },
        ];
    }
    async deployContainer() {
        const config = await this.getDefaultConfig();
        const containerName = this.getContainerName();
        const dataDir = await this.getPlatformDataDir('mariadb');
        logger.info(`Deploying MariaDB 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 MariaDB data
        try {
            const stat = await plugins.fs.stat(`${dataDir}/ibdata1`);
            dataExists = stat.isFile;
            logger.info(`MariaDB data directory exists with ibdata1 file`);
        }
        catch {
            // ibdata1 file doesn't exist, this is a fresh install
            dataExists = false;
        }
        if (platformService?.adminCredentialsEncrypted) {
            // Reuse existing credentials from database
            logger.info('Reusing existing MariaDB credentials (data directory already initialized)');
            adminCredentials = await credentialEncryption.decrypt(platformService.adminCredentialsEncrypted);
        }
        else {
            // Generate new credentials for fresh deployment
            logger.info('Generating new MariaDB admin credentials');
            adminCredentials = {
                username: 'root',
                password: credentialEncryption.generatePassword(32),
            };
            // If data exists but we don't have credentials, we need to wipe the data
            if (dataExists) {
                logger.warn('MariaDB 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 MariaDB data directory: ${getErrorMessage(e)}`);
                    throw new Error('Cannot deploy MariaDB: 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 MariaDB data directory: ${getErrorMessage(e)}`);
            }
        }
        // Create container using Docker API
        const envVars = [
            `MARIADB_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('/var/lib/mysql', 'mariadb')],
            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(`MariaDB container created: ${containerId}`);
        return containerId;
    }
    async stopContainer(containerId) {
        logger.info(`Stopping MariaDB container ${containerId}...`);
        await this.oneboxRef.docker.stopContainer(containerId);
        logger.success('MariaDB container stopped');
    }
    async healthCheck() {
        try {
            logger.info('MariaDB health check: starting...');
            const platformService = await this.oneboxRef.database.getPlatformServiceByType(this.type);
            if (!platformService) {
                logger.info('MariaDB health check: platform service not found in database');
                return false;
            }
            if (!platformService.adminCredentialsEncrypted) {
                logger.info('MariaDB health check: no admin credentials stored');
                return false;
            }
            if (!platformService.containerId) {
                logger.info('MariaDB health check: no container ID in database record');
                return false;
            }
            logger.info(`MariaDB 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
            const result = await this.oneboxRef.docker.execInContainer(platformService.containerId, ['mariadb-admin', 'ping', '-u', 'root', `-p${adminCreds.password}`]);
            if (result.exitCode === 0) {
                logger.info('MariaDB health check: success');
                return true;
            }
            else {
                logger.info(`MariaDB health check failed: exit code ${result.exitCode}, stderr: ${result.stderr.substring(0, 200)}`);
                return false;
            }
        }
        catch (error) {
            logger.info(`MariaDB 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('MariaDB 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 MariaDB database '${dbName}' for service '${userService.name}'...`);
        // Create database and user via mariadb inside the container
        const sql = [
            `CREATE DATABASE IF NOT EXISTS \`${dbName}\`;`,
            `CREATE USER IF NOT EXISTS '${username}'@'%' IDENTIFIED BY '${password.replace(/'/g, "\\'")}';`,
            `GRANT ALL PRIVILEGES ON \`${dbName}\`.* TO '${username}'@'%';`,
            `FLUSH PRIVILEGES;`,
        ].join(' ');
        const result = await this.oneboxRef.docker.execInContainer(platformService.containerId, [
            'mariadb',
            '-u', 'root',
            `-p${adminCreds.password}`,
            '-e', sql,
        ]);
        if (result.exitCode !== 0) {
            throw new Error(`Failed to provision MariaDB database: exit code ${result.exitCode}, output: ${result.stdout.substring(0, 200)} ${result.stderr.substring(0, 200)}`);
        }
        logger.success(`MariaDB database '${dbName}' provisioned with user '${username}'`);
        // Build the credentials and env vars
        const credentials = {
            host: containerName,
            port: '3306',
            database: dbName,
            username,
            password,
            connectionString: `mysql://${username}:${password}@${containerName}:3306/${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('MariaDB platform service not found or not configured');
        }
        const adminCreds = await credentialEncryption.decrypt(platformService.adminCredentialsEncrypted);
        logger.info(`Deprovisioning MariaDB database '${resource.resourceName}'...`);
        const sql = [
            `DROP USER IF EXISTS '${credentials.username}'@'%';`,
            `DROP DATABASE IF EXISTS \`${resource.resourceName}\`;`,
        ].join(' ');
        const result = await this.oneboxRef.docker.execInContainer(platformService.containerId, [
            'mariadb',
            '-u', 'root',
            `-p${adminCreds.password}`,
            '-e', sql,
        ]);
        if (result.exitCode !== 0) {
            logger.warn(`MariaDB deprovision returned exit code ${result.exitCode}: ${result.stderr.substring(0, 200)}`);
        }
        logger.success(`MariaDB database '${resource.resourceName}' dropped`);
    }
}
//# sourceMappingURL=data:application/json;base64, 