import { execSync } from 'node:child_process';
export class PleskHostingAdapter {
    credentials;
    logger;
    constructor(credentials, logger) {
        this.credentials = credentials;
        this.logger = logger;
    }
    async createDatabase(params) {
        const dbTypeLabel = params.type === 'postgresql' ? 'PostgreSQL' : 'MySQL';
        const spinner = this.logger.step(`Creando base de datos ${dbTypeLabel}: ${params.name}...`);
        // Find DB server
        const serverId = await this.findDbServerId(params.type);
        // Create DB
        const dbResponse = await fetch(this.url('/databases'), {
            body: JSON.stringify({
                name: params.name,
                parent_domain: { id: params.domainId },
                server_id: serverId,
                type: params.type,
            }),
            headers: this.headers(),
            method: 'POST',
        });
        if (!dbResponse.ok) {
            const errorText = await dbResponse.text();
            if (errorText.includes('already exists')) {
                spinner.warn(`La base de datos "${params.name}" ya existe`);
                return;
            }
            spinner.fail('Error al crear base de datos');
            throw new Error(`Plesk API error: ${dbResponse.status} ${errorText}`);
        }
        const db = (await dbResponse.json());
        // Create DB user
        const userResponse = await fetch(this.url('/dbusers'), {
            body: JSON.stringify({
                database_id: db.id,
                login: params.user,
                password: params.password,
            }),
            headers: this.headers(),
            method: 'POST',
        });
        if (!userResponse.ok) {
            const errorText = await userResponse.text();
            spinner.fail('Error al crear usuario de base de datos');
            throw new Error(`Plesk API error: ${userResponse.status} ${errorText}`);
        }
        spinner.succeed(`Base de datos creada: ${params.name} (${dbTypeLabel})`);
    }
    async createSubscription(params) {
        const existing = await this.findDomain(params.domain);
        if (existing) {
            this.logger.warn(`La suscripción "${params.domain}" ya existe (id: ${existing.id})`);
            return { action: 'existing', domainId: existing.id };
        }
        const spinner = this.logger.step(`Creando suscripción: ${params.domain}...`);
        const response = await fetch(this.url('/domains'), {
            body: JSON.stringify({
                hosting_settings: {
                    ftp_login: params.ftpLogin,
                    ftp_password: params.ftpPassword,
                },
                hosting_type: 'virtual',
                name: params.domain,
                plan: { name: params.plan },
            }),
            headers: this.headers(),
            method: 'POST',
        });
        if (!response.ok) {
            spinner.fail('Error al crear suscripción');
            throw new Error(`Plesk API error: ${response.status} ${await response.text()}`);
        }
        const domain = (await response.json());
        spinner.succeed(`Suscripción creada: ${params.domain} (id: ${domain.id})`);
        return { action: 'created', domainId: domain.id };
    }
    async disableProxyMode(params) {
        const spinner = this.logger.step(`Desactivando proxy mode para ${params.domain}...`);
        const xml = `<packet><site><set><filter><name>${params.domain}</name></filter><values><hosting><vrt_hst><property><name>nginx-proxy-mode</name><value>false</value></property></vrt_hst></hosting></values></set></site></packet>`;
        const response = await fetch(this.xmlApiUrl(), {
            body: xml,
            headers: {
                'Content-Type': 'text/xml',
                KEY: this.credentials.apiKey,
            },
            method: 'POST',
        });
        const text = await response.text();
        if (text.includes('<status>error</status>')) {
            const match = text.match(/<errtext>(.+?)<\/errtext>/);
            spinner.fail('Error al desactivar proxy mode');
            throw new Error(`Plesk XML API error: ${match ? match[1] : 'Unknown error'}`);
        }
        spinner.succeed(`Proxy mode desactivado para ${params.domain}`);
    }
    async enableNodeJs(params) {
        // 1. Enable Node.js via XML API
        const enableSpinner = this.logger.step(`Habilitando Node.js para ${params.domain}...`);
        await this.callNodeJsXmlApi('enable', params.domain);
        enableSpinner.succeed(`Node.js habilitado para ${params.domain}`);
        // 2. Set latest LTS version via XML API
        const version = await this.findLatestLtsVersion();
        if (version) {
            const versionSpinner = this.logger.step(`Estableciendo Node.js ${version}...`);
            await this.callNodeJsXmlApi('set-version', params.domain, { tag: 'version', value: version });
            versionSpinner.succeed(`Node.js ${version} configurado`);
        }
        // 3. Set startup file
        const startupSpinner = this.logger.step(`Estableciendo archivo de inicio ${params.startupFile}...`);
        await this.callNodeJsXmlApi('set-startup-file', params.domain, { tag: 'startup-file', value: params.startupFile });
        startupSpinner.succeed(`Archivo de inicio configurado: ${params.startupFile}`);
    }
    async enableShellAccess(params) {
        const spinner = this.logger.step(`Configurando acceso SSH (/bin/bash) para ${params.domain}...`);
        try {
            this.execSsh(params.sshUser, `plesk bin subscription --update ${params.domain} -shell /bin/bash`);
            spinner.succeed(`Acceso SSH (/bin/bash) configurado para ${params.domain}`);
        }
        catch {
            spinner.fail('Error al configurar acceso SSH');
            throw new Error(`No se pudo configurar el shell /bin/bash para ${params.domain}`);
        }
    }
    async getAdminEmail(params) {
        const output = this.execSsh(params.sshUser, 'plesk bin admin --info');
        const match = output.match(/[Ee]mail:\s*(.+)/);
        if (!match?.[1]?.trim()) {
            throw new Error("No se pudo obtener el email del administrador de Plesk. Configura 'email' en project dentro de aurora.yaml.");
        }
        return match[1].trim();
    }
    async installSshKey(params) {
        const spinner = this.logger.step(`Instalando clave SSH para ${params.ftpLogin}@${params.domain}...`);
        const sshDir = `/var/www/vhosts/${params.domain}/.ssh`;
        const b64 = Buffer.from(params.publicKey.trim()).toString('base64');
        try {
            this.execSsh(params.sshUser, `mkdir -p ${sshDir} && echo '${b64}' | base64 -d >> ${sshDir}/authorized_keys && chown -R ${params.ftpLogin}:psaserv ${sshDir} && chmod 700 ${sshDir} && chmod 600 ${sshDir}/authorized_keys`);
            spinner.succeed(`Clave SSH instalada para ${params.ftpLogin}@${params.domain}`);
        }
        catch (error) {
            spinner.warn('No se pudo instalar la clave SSH automáticamente.');
            this.logger.logError(`SSH key installation failed for ${params.domain}`, error);
            this.logger.info('Añade manualmente esta clave pública al servidor:');
            this.logger.info(params.publicKey);
        }
    }
    async installSslCertificate(params) {
        const spinner = this.logger.step(`Instalando certificado SSL (Let's Encrypt) para ${params.domain}...`);
        try {
            this.execSsh(params.sshUser, `plesk bin extension --exec letsencrypt cli.php -d ${params.domain} -m ${params.email}`);
            spinner.succeed(`Certificado SSL instalado para ${params.domain}`);
        }
        catch (error) {
            spinner.warn(`No se pudo instalar el certificado SSL para ${params.domain}`);
            this.logger.logError(`SSL installation failed for ${params.domain}`, error);
            this.logger.info('Es posible que el DNS aún no haya propagado. Puedes instalarlo manualmente desde Plesk o re-ejecutar con --skip-dns --skip-cicd --skip-ssh.');
        }
    }
    async setNginxDirectives(params) {
        const spinner = this.logger.step(`Configurando directivas nginx para ${params.domain}...`);
        const confPath = `/var/www/vhosts/system/${params.domain}/conf/vhost_nginx.conf`;
        const b64 = Buffer.from(params.directives).toString('base64');
        try {
            this.execSsh(params.sshUser, `echo '${b64}' | base64 -d > ${confPath} && nginx -t && service nginx reload`);
            spinner.succeed(`Directivas nginx configuradas para ${params.domain}`);
        }
        catch {
            spinner.fail('Error al configurar directivas nginx');
            throw new Error(`No se pudieron aplicar las directivas nginx en ${confPath}. Verifica que el fichero nginx.conf sea válido.`);
        }
    }
    async callNodeJsXmlApi(operation, domain, value) {
        const valueNode = value ? `<${value.tag}>${value.value}</${value.tag}>` : '';
        const xml = `<packet><extension><call><nodejs><${operation}>${valueNode}<domain>${domain}</domain></${operation}></nodejs></call></extension></packet>`;
        const response = await fetch(this.xmlApiUrl(), {
            body: xml,
            headers: {
                'Content-Type': 'text/xml',
                KEY: this.credentials.apiKey,
            },
            method: 'POST',
        });
        const text = await response.text();
        if (text.includes('<status>error</status>')) {
            const match = text.match(/<errtext>(.+?)<\/errtext>/);
            throw new Error(`Plesk XML API error: ${match ? match[1] : 'Unknown error'}`);
        }
    }
    execSsh(user, command) {
        const keyFlag = this.credentials.sshKey
            ? `-i ${this.credentials.sshKey} `
            : '';
        const cmd = `ssh ${keyFlag}-o StrictHostKeyChecking=no ${user}@${this.credentials.host} "${command}"`;
        return execSync(cmd, { stdio: 'pipe' }).toString();
    }
    async findDbServerId(type) {
        const response = await fetch(this.url('/dbservers'), {
            headers: this.headers(),
        });
        if (!response.ok) {
            throw new Error(`Plesk API error: ${response.status} ${response.statusText}`);
        }
        const servers = (await response.json());
        const server = servers.find((s) => s.type === type);
        if (!server) {
            throw new Error(`No database server found for type "${type}"`);
        }
        return server.id;
    }
    async findDomain(domainName) {
        const response = await fetch(this.url('/domains'), {
            headers: this.headers(),
        });
        if (!response.ok) {
            throw new Error(`Plesk API error: ${response.status} ${response.statusText}`);
        }
        const domains = (await response.json());
        return domains.find((d) => d.name === domainName);
    }
    async findLatestLtsVersion() {
        const xml = '<packet><extension><call><nodejs><versions/></nodejs></call></extension></packet>';
        const response = await fetch(this.xmlApiUrl(), {
            body: xml,
            headers: {
                'Content-Type': 'text/xml',
                KEY: this.credentials.apiKey,
            },
            method: 'POST',
        });
        const text = await response.text();
        // Parse enabled versions from XML
        const versionRegex = /<version><enabled>true<\/enabled><version>(\d+\.\d+\.\d+)<\/version>/g;
        const versions = [];
        let match;
        while ((match = versionRegex.exec(text)) !== null) {
            versions.push(match[1]);
        }
        // Filter LTS (even major) and pick the highest
        const ltsVersions = versions
            .filter((v) => Number.parseInt(v.split('.')[0], 10) % 2 === 0)
            .sort((a, b) => Number.parseInt(b.split('.')[0], 10) -
            Number.parseInt(a.split('.')[0], 10));
        return ltsVersions[0];
    }
    headers() {
        return {
            Accept: 'application/json',
            'Content-Type': 'application/json',
            'X-API-Key': this.credentials.apiKey,
        };
    }
    url(path) {
        return `https://${this.credentials.host}:8443/api/v2${path}`;
    }
    xmlApiUrl() {
        return `https://${this.credentials.host}:8443/enterprise/control/agent.php`;
    }
}
