/*
 * Copyright (C) 2024-present Puter Technologies Inc.
 *
 * This file is part of Puter.
 *
 * Puter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
'use strict';
const eggspress = require('../../api/eggspress.js');
const APIError = require('../../api/APIError.js');
const { Context } = require('../../util/context.js');
const FSNodeParam = require('../../api/filesystem/FSNodeParam.js');
const { DB_WRITE } = require('../../services/database/consts.js');

// -----------------------------------------------------------------------//
// POST /rename
// -----------------------------------------------------------------------//
module.exports = eggspress('/rename', {
    subdomain: 'api',
    auth2: true,
    verified: true,
    fs: true,
    json: true,
    allowedMethods: ['POST'],
    alias: { uid: 'path' },
    parameters: {
        subject: new FSNodeParam('path'),
    },
}, async (req, res, next) => {
    if ( ! req.body.new_name ) {
        throw APIError.create('field_missing', null, {
            key: 'new_name',
        });
    }
    if ( typeof req.body.new_name !== 'string' ) {
        throw APIError.create('field_invalid', null, {
            key: 'new_name',
            expected: 'string',
            got: typeof req.body.new_name,
        });
    }

    // modules
    const db = req.services.get('database').get(DB_WRITE, 'filesystem');
    const mime = require('mime-types');
    const { get_app, validate_fsentry_name, id2path } = require('../../helpers.js');
    const _path = require('path');

    // new_name validation
    try {
        validate_fsentry_name(req.body.new_name);
    } catch (e) {
        return res.status(400).send({
            error: {
                message: e.message,
            },
        });
    }

    const { subject } = req.values;

    //get fsentry
    if ( ! await subject.exists() ) {
        throw APIError.create('subject_does_not_exist');
    }

    // Access control
    {
        const actor = Context.get('actor');
        const svc_acl = Context.get('services').get('acl');
        if ( ! await svc_acl.check(actor, subject, 'write') ) {
            throw await svc_acl.get_safe_acl_error(actor, subject, 'write');
        }
    }

    await subject.fetchEntry();
    let fsentry = subject.entry;

    // immutable
    if ( fsentry.immutable ) {
        return res.status(400).send({
            error: {
                message: 'Immutable: cannot rename.',
            },
        });
    }

    let res1;

    // parent is root
    if ( fsentry.parent_uid === null ) {
        try {
            res1 = await db.read('SELECT uuid FROM fsentries WHERE parent_uid IS NULL AND name = ? AND id != ? LIMIT 1',
                            [
                                //name
                                req.body.new_name,
                                await subject.get('mysql-id'),
                            ]);
        } catch (e) {
            console.log(e);
        }
    }
    // parent is regular dir
    else {
        res1 = await db.read('SELECT uuid FROM fsentries WHERE parent_uid = ? AND name = ? AND id != ? LIMIT 1',
                        [
                            //parent_uid
                            fsentry.parent_uid,
                            //name
                            req.body.new_name,
                            await subject.get('mysql-id'),
                        ]);
    }
    if ( res1[0] ) {
        throw APIError.create('item_with_same_name_exists', null, {
            entry_name: req.body.new_name,
        });
    }

    const old_path = await id2path(await subject.get('mysql-id'));
    const new_path = _path.join(_path.dirname(old_path), req.body.new_name);

    // update `name`
    await db.write('UPDATE fsentries SET name = ?, path = ? WHERE id = ?',
                    [req.body.new_name, new_path, await subject.get('mysql-id')]);

    const filesystem = req.services.get('filesystem');
    await filesystem.update_child_paths(old_path, new_path, req.user.id);

    // associated_app
    let associated_app;
    if ( fsentry.associated_app_id ) {
        const app = await get_app({ id: fsentry.associated_app_id });
        // remove some privileged information
        delete app.id;
        delete app.approved_for_listing;
        delete app.approved_for_opening_items;
        delete app.godmode;
        delete app.owner_user_id;
        // add to array
        associated_app = app;
    } else {
        associated_app = {};
    }

    // send the fsentry of the new object created
    const contentType = mime.contentType(req.body.new_name);
    const return_obj = {
        uid: req.body.uid,
        name: req.body.new_name,
        is_dir: fsentry.is_dir,
        path: new_path,
        old_path: old_path,
        type: contentType || null,
        associated_app: associated_app,
        original_client_socket_id: req.body.original_client_socket_id,
    };

    // send realtime success msg to client
    const svc_socketio = req.services.get('socketio');
    svc_socketio.send({ room: req.user.id }, 'item.renamed', return_obj);

    (async () => {
        try {
            const svc_event = req.services.get('event');
            await svc_event.emit('fs.rename', {
                uid: fsentry.uuid,
                new_name: req.body.new_name,
            });
        } catch (e) {
            const log = req.services.get('log-service').create('rename-endpoint');
            const errors = req.services.get('error-service').create(log);
            errors.report('emit.rename', {
                alarm: true,
                source: e,
            });
        }
    })();

    return res.send(return_obj);
});
