/** @odoo-module alias=vault.export **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import {_lt} from "@web/core/l10n/translation";
import utils from "vault.utils";

// This class handles the export to different formats by using a standardize
// JSON formatted data generated by the python backend.
//
// JSON format description:
//
// Entries are represented as objects with the following attributes
//  `name`, `uuid`, `url`, `note`
//     Specific fields of the entry. `uuid` is used for updating existing records
//  `childs`
//     Child entries
//  `fields`, `files`
//     List of encypted fields/files with `name`, `iv`, and `value`
//
export default class VaultExporter {
    /**
     * Encrypt a field of the above format properly for the backend to store.
     * The changes are done inplace.
     *
     * @private
     * @param {CryptoKey} master_key
     * @param {Object} node
     */
    async _export_json_entry(master_key, node) {
        const fields = [];
        for (const field of node.fields || [])
            fields.push({
                name: field.name,
                value: await utils.sym_decrypt(master_key, field.value, field.iv),
            });

        const files = [];
        for (const file of node.files || [])
            files.push({
                name: file.name,
                value: await utils.sym_decrypt(master_key, file.value, file.iv),
            });

        const childs = [];
        for (const entry of node.childs || [])
            childs.push(await this._export_json_entry(master_key, entry));

        return {
            name: node.name || "",
            uuid: node.uuid || null,
            url: node.url || "",
            note: node.note || "",
            childs: childs,
            fields: fields,
            files: files,
        };
    }

    /**
     * Decrypt the data fro the JSON export.
     *
     * @private
     * @param {CryptoKey} master_key
     * @param {Object} data
     * @returns the encrypted entry for the database
     */
    async _export_json_data(master_key, data) {
        const result = [];
        for (const node of data)
            result.push(await this._export_json_entry(master_key, node));
        return JSON.stringify(result);
    }

    /**
     * Export using JSON format. The database is stored in the `data` field of the JSON
     * type and is an encrypted JSON object. For the encryption the needed encryption
     * parameter `iv`, `salt` and `iterations` are stored in the file.
     * This will add `iv` to fields and files and encrypt the `value`
     *
     * @private
     * @param {CryptoKey} master_key
     * @param {String} data
     * @returns the encrypted entry for the database
     */
    async _export_json(master_key, data) {
        // Get the password for the exported file from the user
        const askpass = await utils.askpass(
            _lt("Please enter the password for the database")
        );
        let password = askpass.password || "";
        if (askpass.keyfile)
            password += await utils.digest(utils.toBinary(askpass.keyfile));

        const iv = utils.generate_iv_base64();
        const salt = utils.generate_bytes(utils.SaltLength).buffer;
        const iterations = utils.Derive.iterations;
        const key = await utils.derive_key(password, salt, iterations);

        // Unwrap the master key and decrypt the entries
        const content = await this._export_json_data(master_key, JSON.parse(data));
        return {
            type: "encrypted",
            iv: iv,
            salt: utils.toBase64(salt),
            data: await utils.sym_encrypt(key, content, iv),
            iterations: iterations,
        };
    }

    /**
     * The main export functions which checks the file ending and calls the right function
     * to handle the rest of the export
     *
     * @private
     * @param {CryptoKey} master_key
     * @param {String} filename
     * @param {String} content
     * @returns the data importable by the backend or false on error
     */
    async export(master_key, filename, content) {
        if (!utils.supported()) return false;

        if (filename.endsWith(".json"))
            return await this._export_json(master_key, content);
        return false;
    }
}
