/*
 * 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/>.
 */
// records app opens

"use strict"
const express = require('express');
const router = express.Router();
const config = require('../config');
const { is_valid_uuid4, get_app } = require('../helpers');
const { DB_WRITE } = require('../services/database/consts.js');
const configurable_auth = require('../middleware/configurable_auth.js');
const { UserActorType, AppUnderUserActorType } = require('../services/auth/Actor.js');
const APIError = require('../api/APIError.js');

// -----------------------------------------------------------------------//
// POST /rao
// -----------------------------------------------------------------------//
router.post('/rao', configurable_auth(), express.json(), async (req, res, next)=>{
    const { actor } = req;
    // check subdomain
    if(require('../helpers').subdomain(req) !== 'api')
        next();

    // check if user is verified
    if((config.strict_email_verification_required || req.user.requires_email_confirmation) && !req.user.email_confirmed)
        return res.status(400).send({code: 'account_is_not_verified', message: 'Account is not verified'});

    let app_uid;
    if ( actor.type instanceof UserActorType )  {
        // validation
        if(!req.body.app_uid || typeof req.body.app_uid !== 'string' && !(req.body.app_uid instanceof String))
            return res.status(400).send({code: 'invalid_app_uid', message: 'Invalid app uid'});
        // must be a valid uuid
        // app uuids start with 'app-', so in order to validate them we remove the prefix first
        else if(!is_valid_uuid4(req.body.app_uid.replace('app-','')))
            return res.status(400).send({code: 'invalid_app_uid', message: 'Invalid app uid'});
        
        app_uid = req.body.app_uid;
    } else if ( actor.type instanceof AppUnderUserActorType ) {
        app_uid = actor.type.app.uid;
    } else {
        throw APIError.create('forbidden');
    }

    // get db connection
    const db = req.services.get('database').get(DB_WRITE, 'apps');

    // insert into db
    db.write(
        `INSERT INTO app_opens (app_uid, user_id, ts) VALUES (?, ?, ?)`,
        [app_uid, req.user.id, Math.floor(new Date().getTime() / 1000)]
    )

    // get app
    const opened_app = await get_app({uid: app_uid});

    // send process event `puter.app_open`
    process.emit('puter.app_open', {
        app_uid: app_uid,
        user_id: req.user.id,
        app_owner_user_id: opened_app.owner_user_id,
        ts: Math.floor(new Date().getTime() / 1000)
    });

    // -----------------------------------------------------------------------//
    // Update the 'app opens' cache
    // -----------------------------------------------------------------------//
    // First try the cache to see if we have recent apps
    let recent_apps = kv.get('app_opens:user:' + req.user.id);

    // If cache is not empty, prepend it with the new app
    if(recent_apps && recent_apps.length > 0){
        // add the app to the beginning of the array
        recent_apps.unshift({app_uid: app_uid});

        // dedupe the array
        recent_apps = recent_apps.filter((v,i,a)=>a.findIndex(t=>(t.app_uid === v.app_uid))===i);

        // limit to 10
        recent_apps = recent_apps.slice(0, 10);

        // update cache
        kv.set('app_opens:user:' + req.user.id, recent_apps);
    }
    // Cache is empty, query the db and update the cache
    else{
        db.read(
            'SELECT DISTINCT app_uid FROM app_opens WHERE user_id = ? GROUP BY app_uid ORDER BY MAX(_id) DESC LIMIT 10',
            [req.user.id]).then( ([apps]) => {
                // Update cache with the results from the db (if any results were returned)
                if(apps && Array.isArray(apps) && apps.length > 0){
                    kv.set('app_opens:user:' + req.user.id, apps);
                }
            });
    }

    // Update clients
    const svc_socketio = req.services.get('socketio');
    svc_socketio.send({ room: req.user.id }, 'app.opened', {
        uuid: opened_app.uid,
        uid: opened_app.uid,
        name: opened_app.name,
        title: opened_app.title,
        icon: opened_app.icon,
        godmode: opened_app.godmode,
        maximize_on_start: opened_app.maximize_on_start,
        index_url: opened_app.index_url,
        original_client_socket_id: req.body.original_client_socket_id,
    });

    // return
    return res.status(200).send({code: 'ok', message: 'ok'});
});

module.exports = router