'use strict';

const child_process = require('node:child_process'),
    crypto = require('node:crypto'),
    fs = require('node:fs'),
    path = require('node:path'),
    paths = require('../src/paths.js').default;

async function deleteOldSettings(db) {
    await db.runSql('DELETE FROM settings WHERE name=? OR name=? OR name=?', [ 'backup_storage', 'backup_limits', 'backup_policy' ]);
}

exports.up = async function (db) {
    const cmd = 'CREATE TABLE IF NOT EXISTS backupSites(' +
        'id VARCHAR(128) NOT NULL UNIQUE,' +
        'name VARCHAR(128) NOT NULL,' +
        'provider VARCHAR(32) NOT NULL,' +
        'configJson TEXT,' +
        'limitsJson TEXT,' +
        'retentionJson TEXT,' +
        'encryptionJson TEXT,' +
        'integrityKeyPairJson TEXT,' +
        'format VARCHAR(16) NOT NULL,' +
        'schedule VARCHAR(128),' +
        'enableForUpdates BOOLEAN DEFAULT false,' +
        'contentsJson TEXT,' +
        'creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,' +
        'ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,' +
        'PRIMARY KEY (id)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin';

    await db.runSql(cmd);

    const results = await db.runSql('SELECT name, value FROM settings WHERE name=? OR name=? OR name=?', [ 'backup_storage', 'backup_limits', 'backup_policy' ]);

    const domainCountResults = await db.runSql('SELECT COUNT(*) AS total FROM domains');
    if (domainCountResults[0].total === 0) {
        console.log('This cloudron is not activated. Deleting the default backup config from 20171205124434-settings-default-backupConfig.js'); // will be added at provision time
        await deleteOldSettings(db);
        return;
    }

    const name = 'Default', enableForUpdates = true;
    let config = null, limits = null, encryption = null, format = null, provider = null;
    let retention = { keepWithinSecs: 2 * 24 * 60 * 60 };
    let schedule = '00 00 23 * * *';
    const integrityKeyPair = crypto.generateKeyPairSync('ed25519', {
        publicKeyEncoding: { type: 'spki', format: 'pem' },
        privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
    });

    const id = crypto.randomUUID();

    // convert existing configuration into a backup target
    for (const r of results) {
        if (r.name === 'backup_storage') {
            const tmp = JSON.parse(r.value);
            // provider is top level
            provider = tmp.provider;

            if (provider === 'noop') return await deleteOldSettings(db); // noop is migrated as 0 sites. user has to manually update with skipBackup

            // the s3 and filesystem backend use the _provider internal property
            if (provider !== 'gcs') tmp._provider = tmp.provider;
            delete tmp.provider;

            // backupFolder is now backupDir
            if ('backupFolder' in tmp) {
                tmp.backupDir = tmp.backupFolder;
                delete tmp.backupFolder;
            }

            // encryption is not part of config anymore, it is top level
            encryption = tmp.encryption || null;
            delete tmp.encryption;

            // format is not part of config anymore, it is top level
            format = tmp.format;
            delete tmp.format;

            // previous releases only had a single "managed" mount at /mnt/cloudronbackup .
            // new release has it under /mnt/managedbackups .
            if (tmp.mountOptions) tmp._managedMountPath = '/mnt/cloudronbackup';

            config = tmp;
        } else if (r.name === 'backup_limits') {
            limits = JSON.parse(r.value);
        } else if (r.name === 'backup_policy') {
            const tmp = JSON.parse(r.value);
            retention = tmp.retention;
            schedule = tmp.schedule;
        }
    }

    const targetInfoDir = path.join(paths.BACKUP_INFO_DIR, id);
    console.log(`Moving existing cache and snapshot file into ${targetInfoDir}`);
    fs.mkdirSync(targetInfoDir, { recursive: true }); // this gets chown'ed by start.sh
    child_process.execSync(`find ${paths.BACKUP_INFO_DIR}/ -maxdepth 1 -type f -exec mv -t ${targetInfoDir}/ {} +`);

    console.log(`Delete any existing rsync cache files since old one has no integrity information`);
    child_process.execSync(`rm -f ${targetInfoDir}/*.cache`);

    await db.runSql('START TRANSACTION');
    await db.runSql('INSERT INTO backupSites (id, name, provider, configJson, limitsJson, integrityKeyPairJson, retentionJson, schedule, encryptionJson, format, enableForUpdates) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
        [ id, name, provider, JSON.stringify(config), JSON.stringify(limits), JSON.stringify(integrityKeyPair), JSON.stringify(retention), schedule, encryption ? JSON.stringify(encryption) : null, format, enableForUpdates ]);
    await deleteOldSettings(db);
    await db.runSql('UPDATE tasks SET type=? WHERE type=?', [ `backup_${id}`, 'backup' ]); // migrate the tasks
    await db.runSql('COMMIT');
};

exports.down = async function (db) {
    await db.runSql('DROP TABLE backupSites');
};
