const express = require('express');
const router = express.Router(),
  fs = require('fs'),
  path = require('path');

const telegrambot = require('../telegram/telegrambot');

const i18n = require('i18n');

const sharp = require('sharp');

const printf = require('util').format;

const {
  authenticate,
  authenticate_noerror,
  authenticate_noerror_WithUser,
  authenticate_noerror_WithUserLean,
} = require('../middleware/authenticate');

const { ObjectId } = require('mongodb');

// const {ListaIngresso} = require('../models/listaingresso');
const { Graduatoria } = require('../models/graduatoria');

const mongoose = require('mongoose').set('debug', false);
const { CfgServer } = require('../models/cfgserver');

// const uuidv4 = require('uuid/v4'); // I chose v4 ‒ you can select others

// const ftp = require('../ftp/FTPClient');
const formidable = require('formidable');
const folder = path.join(__dirname, 'upload');

const sanitizeHtml = require('sanitize-html');

if (!fs.existsSync(folder)) {
  fs.mkdirSync(folder);
}

const _ = require('lodash');

const { User } = require('../models/user');
const { MyGroup } = require('../models/mygroup');
const { Circuit } = require('../models/circuit');
const { Booking } = require('../models/booking');
const { Operator } = require('../models/operator');
const { Where } = require('../models/where');
const { MyEvent } = require('../models/myevent');
const { Contribtype } = require('../models/contribtype');
const { PaymentType } = require('../models/paymenttype');
const { Discipline } = require('../models/discipline');
const { MyElem } = require('../models/myelem');
const { Cron } = require('../models/cron');
const { Skill } = require('../models/skill');
const { Good } = require('../models/good');
const { Bacheca } = require('../models/bacheca');
const { StatusSkill } = require('../models/statusSkill');
const { Province } = require('../models/province');
const { City } = require('../models/city');
const { Sector } = require('../models/sector');
const { SectorGood } = require('../models/sectorgood');
const { SectorBacheca } = require('../models/sectorbacheca');
const { CatGrp } = require('../models/catgrp');
const Site = require('../models/site');
const { Level } = require('../models/level');
const { AdType } = require('../models/adtype');
const { AdTypeGood } = require('../models/adtypegood');
const { Newstosent } = require('../models/newstosent');
const { MyPage } = require('../models/mypage');
const { CalZoom } = require('../models/calzoom');
const { Gallery } = require('../models/gallery');
const { Settings } = require('../models/settings');
const { SendMsg } = require('../models/sendmsg');
const { SendNotif } = require('../models/sendnotif');
const { Permission } = require('../models/permission');
const Producer = require('../models/producer');
const Cart = require('../models/cart');
const OrdersCart = require('../models/orderscart');
const Storehouse = require('../models/storehouse');
const Provider = require('../models/provider');
const CatProd = require('../models/catprod');
const CatAI = require('../models/catai');
const SubCatProd = require('../models/subcatprod');
const Gasordine = require('../models/gasordine');
const Product = require('../models/product');
const Author = require('../models/author');
const Collana = require('../models/collana');
const { Catalog } = require('../models/catalog');
const { RaccoltaCataloghi } = require('../models/raccoltacataloghi');
const Publisher = require('../models/publisher');
const Scontistica = require('../models/scontistica');
const Department = require('../models/department');
const { Category } = require('../models/category');
const Group = require('../models/group');

const T_WEB_StatiProdotto = require('../models/t_web_statiprodotto');
const T_WEB_Tipologie = require('../models/t_web_tipologie');
const T_WEB_TipiFormato = require('../models/t_web_tipiformato');

const tools = require('../tools/general');

const server_constants = require('../tools/server_constants');
const actions = require('./api/actions');

const shared_consts = require('../tools/shared_nodejs');

const globalTables = require('../tools/globalTables');

const UserCost = {
  FIELDS_UPDATE_TELEGRAM_BOT: [
    'profile.teleg_id',
    'profile.manage_telegram',
    'profile.admin_telegram',
    'deleted',
    'reported',
  ],

  FIELDS_REQUISITI: [
    'verified_email',
    'profile.teleg_id',
    'profile.saw_and_accepted',
    'profile.revolut',
    'profile.payeer_id',
    'profile.advcash_id',
    'profile.link_payment',
    'profile.email_paypal',
    'profile.paymenttypes',
  ],
};

router.post('/ammetti', (req, res) => {
  const body = _.pick(req.body, ['idapp', 'token', 'username']);
  const idapp = body.idapp;
  const token = body.token;
  const username = body.username;

  try {
    // Cerco il token se è ancora da ammettere

    User.findAmmissioneByTokenAndUsername(idapp, token, username)
      .then(async (user) => {
        if (!user) {
          return res.status(404).send();
        } else {
          const lang = user.lang;
          console.log('user', user);
          // user.verified_by_aportador = false;
          if (user.verified_by_aportador) {
            return res.send({
              code: server_constants.RIS_CODE_GIA_AMMESSO,
              msg: 'Il membro ' + user.username + ' è stato già Ammesso!',
            });
          } else {
            user.verified_by_aportador = true;

            const userDest = await User.getUserShortDataByUsername(user.idapp, user.aportador_solidario);
            const myuser = user;
            const dati = {
              token,
            };

            const msgOrig = i18n.__(
              { phrase: '✅ Sei stato Ammesso correttamente da %s!', locale: lang },
              userDest.username
            );
            const msgDest = i18n.__(
              { phrase: '✅ %s è stato Ammesso correttamente (da %s)!', locale: lang },
              myuser.username,
              userDest.username
            );

            const sendemail = require('../sendemail');

            // Invia una email alla persona che è stata ammessa
            const ris = await sendemail.sendEmail_Utente_Ammesso(user.lang, userDest.email, userDest, user.idapp, dati);

            await telegrambot.sendMsgTelegram(user.idapp, myuser.username, msgOrig);
            await telegrambot.sendMsgTelegram(user.idapp, userDest.username, msgDest);
            // Invia questo msg anche all'Admin
            await telegrambot.sendMsgTelegramToTheAdmin(user.idapp, msgDest, userDest.username);

            // user.token_da_ammettere = 'OK';
            user.save().then(() => {
              return res.send({
                code: server_constants.RIS_CODE_AMMESSO,
                msg: 'Ottimo! Hai ammesso ' + user.username + '!',
                //msg: tools.getres__('Ottimo! Hai ammesso', res) + ' ' + user.username + '!',
              });
            });
          }
        }
      })
      .catch((e) => {
        console.log('Errore Ammissione:', e.message);
        res.status(400).send();
      });
  } catch (e) {
    console.error('Errore: ', e);
    res.status(400).send();
  }
});

router.post('/abcirc', async (req, res) => {
  const body = _.pick(req.body, ['idapp', 'token', 'username', 'username_action', 'cmd', 'groupname']);
  const idapp = body.idapp;
  const token = body.token;
  const username = body.username;
  const groupname = body.groupname;
  const username_action = body.username_action;
  const cmd = parseInt(body.cmd);


  try {
    // Cerco il token se è ancora da ammettere

    await User.getCircuitByTokenAndUsername(idapp, username, token)
      .then(async (ris) => {
        const { circuitname, user } = ris;
        if (!user) {
            return res.send({
              code: server_constants.RIS_CODE_ERRORE,
              msg: 'La richiesta di Abilitazione non è più presente su questo circuito !',
            });
        } else {
          const lang = user.lang;
          // console.log('user', user);

          let nomeCircuito = circuitname;
          let giaabilitato = await User.isFidoConcesso(idapp, username, nomeCircuito);

          let ret = null;

          // user.verified_by_aportador = false;
          if (giaabilitato) {
            return res.send({
              code: server_constants.RIS_CODE_GIA_AMMESSO,
              msg: 'Il membro <strong>' + user.username + '</strong> è già stato abilitato al circuito ' + nomeCircuito + '!',
              circuitName: nomeCircuito,
            });
          } else {
            ret = await User.setCircuitCmd(user.idapp, username, nomeCircuito, cmd, 0, username_action, {
              groupname,
            });
            return res.send({
              code: server_constants.RIS_CODE_AMMESSO,
              msg: 'Ottimo! Hai abilitato ' + user.username + ' al circuito ' + nomeCircuito + '!',
              circuitName: nomeCircuito,
              //msg: tools.getres__('Ottimo! Hai ammesso', res) + ' ' + user.username + '!',
            });
          }
          
        }
      })
      .catch((e) => {
        console.log('Errore Abilitazione Circuito:', e.message);
        res.status(400).send();
      });
  } catch (e) {
    console.error('Errore: ', e);
    res.status(400).send();
  }
});

router.post(process.env.LINKVERIF_REG, (req, res) => {
  const body = _.pick(req.body, ['idapp', 'idlink']);
  const idapp = body.idapp;
  const idlink = body.idlink;

  // Cerco l'idlink se è ancora da Verificare

  User.findByLinkreg(idapp, idlink)
    .then((user) => {
      if (!user) {
        //console.log("NON TROVATO!");
        return res.status(404).send();
      } else {
        console.log('user', user);
        if (user.verified_email) {
          res.send({
            code: server_constants.RIS_CODE_EMAIL_ALREADY_VERIFIED,
            msg: tools.getres__("L'Email è già stata Verificata", res),
          });
        } else {
          user.verified_email = true;
          user.lasttimeonline = new Date();
          user.save().then(() => {
            //console.log("TROVATOOOOOO!");
            res.send({
              code: server_constants.RIS_CODE_EMAIL_VERIFIED,
              msg: tools.getres__('EMAIL', res) + ' ' + tools.getres__('VERIF', res),
            });
          });
        }
      }
    })
    .catch((e) => {
      console.log(process.env.LINKVERIF_REG, e.message);
      res.status(400).send();
    });
});

router.post(process.env.CHECKREVERIF_EMAIL, (req, res) => {
  const body = _.pick(req.body, ['idapp', 'idlink']);
  const idapp = body.idapp;
  const idlink = body.idlink;

  // Cerco l'idlink se è ancora da Verificare

  User.findByLinkVerifEmail(idapp, idlink)
    .then((user) => {
      if (!user) {
        //console.log("NON TROVATO!");
        return res.status(404).send({code: RIS_CODE_ERRORE, msg: 'Verifica email non andata a buon fine. Ripetere.'});
      } else {
        console.log('user', user);
        if (user.verified_email) {
          res.send({
            code: server_constants.RIS_CODE_EMAIL_ALREADY_VERIFIED,
            msg: tools.getres__("L'Email è già stata Verificata", res),
          });
        } else {
          user.verified_email = true;
          user.lasttimeonline = new Date();
          user.save().then(() => {
            //console.log("TROVATOOOOOO!");
            res.send({
              code: server_constants.RIS_CODE_EMAIL_VERIFIED,
              msg: tools.getres__('EMAIL', res) + ' ' + tools.getres__('VERIF', res),
            });
          });
        }
      }
    })
    .catch((e) => {
      console.log(process.env.LINKVERIF_REG, e.message);
      res.status(400).send();
    });
});

router.post(process.env.ADD_NEW_SITE, async (req, res) => {
  try {
    const body = req.body;
    const idapp = body.idappSent;
    const name = body.name;
    const email = body.email.toLowerCase().trim();

    console.log('Add New Site: idapp = ', idapp, 'email=', email, 'name=', name);

    const ris = await User.addNewSite(idapp, body);

    if (ris) {
      res.send(ris);
    } else {
      res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: 'Errore' });
    }
  } catch (e) {
    console.log(process.env.ADD_NEW_SITE, e.message);
    res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: e });
  }
});

// Faccio richiesta di una Nuova Password
router.post(process.env.LINK_REQUEST_NEWPASSWORD, async (req, res) => {
  try {
    const body = _.pick(req.body, ['idapp', 'email', 'codetocheck']);
    const idapp = body.idapp;
    const email = body.email.toLowerCase().trim();
    const codetocheck = body.codetocheck ? body.codetocheck.trim() : '';

    console.log('Request Reset Pwd:', email, ' idapp=', idapp);

    // Check if too many requests
    if (await User.tooManyReqPassword(idapp, email, true)) {
      let text = 'Troppe richieste di Password: ' + email;
      telegrambot.sendMsgTelegramToTheManagers(idapp, text);
      console.log(process.env.LINK_REQUEST_NEWPASSWORD, text, email);
      res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: text });
      return false;
    }

    console.log('POST ' + process.env.LINK_REQUEST_NEWPASSWORD + ' idapp= ' + idapp + ' email = ' + email);

    const reqpwd = await User.createNewRequestPwd(idapp, email, codetocheck);
    if (reqpwd && reqpwd.ris) {
      res.send({ code: server_constants.RIS_CODE_OK, msg: '', link: reqpwd.link });
    } else {
      return res.status(200).send({ code: server_constants.RIS_CODE_EMAIL_NOT_EXIST, msg: '' });
    }
  } catch (e) {
    console.log(process.env.LINK_REQUEST_NEWPASSWORD, e.message);
    res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: e });
  }
});

// Invio la Nuova Password richiesta dal reset!
// Ritorna il token per poter effettuare le chiamate...
router.post(process.env.LINK_UPDATE_PWD, async (req, res) => {
  try {
    const body = _.pick(req.body, ['idapp', 'email', 'tokenforgot', 'tokenforgot_code', 'password', 'br']);
    const idapp = body.idapp;
    const email = body.email.toLowerCase().trim();
    const browser_random = body.br;
    const tokenforgot = body.tokenforgot;
    const tokenforgot_code = body.tokenforgot_code;
    const password = body.password;
    const msg = 'Richiesta Nuova Password: idapp= ' + idapp + ' email = ' + email;

    console.log(msg);

    // telegrambot.sendMsgTelegramToTheManagers(body.idapp, msg);

    let user = null;

    user = await User.findByLinkTokenforgot(idapp, email, tokenforgot)
      .then((user) => {
        return user;
      })
      .catch((e) => {
        console.log(process.env.LINK_UPDATE_PWD, e.message);
        res.status(400).send();
      });

    if (!user) {
      user = await User.findByLinkTokenforgotCode(idapp, email, tokenforgot_code)
        .then((user) => {
          return user;
        })
        .catch((e) => {
          console.log(process.env.LINK_UPDATE_PWD, e.message);
          res.status(400).send();
        });
    }

    if (!user) {
      return res.send({ code: server_constants.RIS_CODE_TOKEN_RESETPASSWORD_NOT_FOUND });
    } else {
      // aggiorna la nuova password
      user.password = password;
      user.lasttimeonline = new Date();

      // Crea token
      user.generateAuthToken(req).then((ris) => {
        user.tokenforgot = ''; // Svuota il tokenforgot perché non ti servirà più...
        user.tokenforgot_code = ''; // Svuota il tokenforgot perché non ti servirà più...

        // Salva lo User
        user.save().then(() => {
          res
            .header('x-auth', ris.token)
            .header('x-refrtok', ris.refreshToken)
            .header('x-browser-random', ris.browser_random)
            .send({ code: server_constants.RIS_CODE_OK }); // Ritorna il token di ritorno
        });
      });
    }
  } catch (e) {
    console.error('Error: ', e);
  }
});

router.post('/testServer', authenticate_noerror, async (req, res) => {
  try {
    const test = req.body.test;
    let ris = { test };

    if (req.user) {
      await tools.sendNotificationToUser(req.user._id, 'Test Server', 'Test Server OK', '/', '', 'server', []);
    }

    return res.send(ris);
  } catch (e) {
    console.error('testServer', e.message);
    return res.status(400).send(e);
  }
});

router.get('/test1', authenticate_noerror, async (req, res) => {
  try {
    const test = req.query.test;
    let ris = { test };

    if (req.user) {
      await tools.sendNotificationToUser(req.user._id, 'Test Server', 'Test Server OK', '/', '', 'server', []);
    }

    return res.send(ris);
  } catch (e) {
    console.error('testServer', e.message);
    return res.status(400).send(e);
  }
});

router.post('/sendmailreg', authenticate, async (req, res) => {
  try {
    if (!req.user) return res.status(400).send('Mydata VUOTO');

    const sendemail = require('../sendemail');

    const username = req.user.username;
    const idapp = req.user.idapp;

    const user = await User.findOne({
      idapp,
      username,
    });

    let ris = null;

    if (user) {
      const now = new Date();
      const diffTime = Math.abs(now - user.lasttime_email_sent_verify);
      const diffMinutes = Math.ceil(diffTime / (1000 * 60));

      if (diffMinutes < 5) {
        return res
          .status(200)
          .send({ email_inviata: false, error: 'Attendi qualche minuto prima di reinviare nuovamente.' });
      }
      ris = await sendemail.sendEmail_Registration(user.lang, user.email, user, user.idapp, user.linkreg);
      user.lasttime_email_sent_verify = now;
      await user.save();
    }

    return res.status(200).send({ email_inviata: !!ris });
  } catch (e) {
    console.error(e);
  }
});

router.post('/settable', authenticate, async (req, res) => {
  try {
    const params = req.body;
    const mytable = globalTables.getTableByTableName(params.table);

    let mydata = req.body.data;
    let extrarec = {};
    if (mydata && mydata.hasOwnProperty('extrarec')) {
      extrarec = mydata['extrarec'];
      delete mydata['extrarec'];
    }

    if (mydata === undefined) {
      console.error('MYDATA VUOTO !');
      return res.status(400).send('Mydata VUOTO');
    }

    const fieldsvalue = { ALL: 1 };

    mydata.idapp = req.user?.idapp;
    const idapp = mydata.idapp;

    if (req.user && req.user.username) {
      User.setOnLine(req.user.idapp, req.user.username);
    }

    let consentito = false;

    if (
      User.isAdmin(req.user.perm) ||
      User.isManager(req.user.perm) ||
      User.isEditor(req.user.perm) ||
      User.isCommerciale(req.user.perm) ||
      User.isFacilitatore(req.user.perm)
    ) {
      consentito = true;
    }

    if (
      !User.isAdmin(req.user.perm) &&
      !User.isManager(req.user.perm) &&
      !User.isEditor(req.user.perm) &&
      !User.isCommerciale(req.user.perm) &&
      !User.isGrafico(req.user.perm) &&
      !User.isFacilitatore(req.user.perm) &&
      (await !tools.ModificheConsentite(req, params.table, fieldsvalue, mydata ? mydata._id : ''))
    ) {
      // If without permissions, exit
      return res
        .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
        .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
    }

    if (shared_consts.TABLES_USER_ID.includes(params.table)) {
      if (!mydata.userId) mydata.userId = req.user._id;
    }

    if (shared_consts.TABLES_CREATEDBY.includes(params.table)) {
      if (!mydata.createdBy) mydata.createdBy = req.user.username;
    }

    if (shared_consts.TABLES_UPDATE_LASTMODIFIED.includes(params.table)) {
      mydata.date_updated = new Date();
    }

    if (shared_consts.TABLES_PERM_NEWREC.includes(params.table)) {
      if (!consentito) {
        mydata.verifyrec = false;
      }
    }

    if (params.table === shared_consts.TAB_MYGROUPS) {
      if (shared_consts.MYGROUPS_KEY_TO_CRYPTED in mydata) {
        if (mydata[shared_consts.MYGROUPS_KEY_TO_CRYPTED]) {
          mydata[shared_consts.MYGROUPS_KEY_TO_CRYPTED + shared_consts.SUFFIX_CRYPTED] = tools.cryptdata(
            mydata[shared_consts.MYGROUPS_KEY_TO_CRYPTED]
          );
        }
      }
    }

    if (shared_consts.TABLES_USER_INCLUDE_MY.includes(params.table)) {
      if (!mydata.admins || mydata.admins.length <= 0) {
        // Aggiungi solo se non esistono Admin:
        mydata.admins = [];
        const indfind = mydata.admins.findIndex((rec) => rec.username === req.user.username);

        if (indfind < 0) {
          mydata.admins.push({ username: req.user.username });
        }
      }
    }

    delete mydata['__v'];
    delete mydata['__proto__'];

    const isNotNew = mydata['_id'] !== undefined && mydata['_id'] !== 0 && mydata['_id'] !== '';

    let mytablerec = null;

    mytablerec = new mytable(mydata);

    // console.log('mytablerec', mytablerec);

    const mytablestrutt = globalTables.getTableByTableName(params.table);

    if (isNotNew) {
      mytablerec.isNew = false;
    }

    if (
      shared_consts.TABLES_ID_NUMBER.includes(params.table) ||
      shared_consts.TABLES_ID_STRING.includes(params.table)
    ) {
    } else if (params.table === 'hours') {
    } else {
      if (
        (mydata['_id'] === undefined || mydata['_id'] === '' || (mytablerec.isNew && mydata['_id'] === 0)) &&
        (mytablerec._id === undefined || mytablerec._id === '0' || mytablerec._id === 0)
      ) {
        mytablerec._id = new ObjectId();
        mydata._id = new ObjectId();
        mytablerec.isNew = true;
      }
    }

    const isnewrec = mytablerec.isNew;

    if (params.table === shared_consts.TAB_MYGROUPS && isnewrec) {
      // Controlla se esiste già con lo stesso nome
      let alreadyexist = await MyGroup.findOne({ idapp, groupname: mydata.groupname });
      if (alreadyexist) {
        return res.send({ code: server_constants.RIS_CODE_REC_ALREADY_EXIST_CODE });
      }
      alreadyexist = await MyGroup.findOne({ idapp, title: mydata.title });
      if (alreadyexist) {
        return res.send({ code: server_constants.RIS_CODE_REC_ALREADY_EXIST_NAME });
      }
    } else if (params.table === shared_consts.TAB_MYCIRCUITS && isnewrec) {
      // Controlla se esiste già con lo stesso nome
      let alreadyexist = await Circuit.findOne({ idapp, name: mydata.name });
      if (alreadyexist) {
        return res.send({ code: server_constants.RIS_CODE_REC_ALREADY_EXIST_CODE });
      }
      alreadyexist = await Circuit.findOne({ idapp, path: mydata.path });
      if (alreadyexist) {
        return res.send({ code: server_constants.RIS_CODE_REC_ALREADY_EXIST_NAME });
      }
      /*alreadyexist = await Circuit.findOne({idapp, symbol: mydata.symbol});
      if (alreadyexist) {
        return res.send({code: server_constants.RIS_CODE_REC_ALREADY_EXIST_SYMBOL});
      }

       */
    }

    if (shared_consts.TABLES_UPDATE_LASTMODIFIED.includes(params.table)) {
      mytablerec.date_updated = new Date();
      mydata.date_updated = new Date();
    }

    // console.log('mydata',mydata);
    // return await mytablerec.save().
    //  then(async (rec) => {

    const myPromise = new Promise((resolve, reject) => {
      resolve(mytablerec._id && mytable.findById(mytablerec._id));
    });

    // Controlla se esiste già questo record:
    if (shared_consts.TABLES_FIELDS_DESCR_AND_CITY_AND_USER.includes(params.table)) {
      if (mytablerec.isNew) {
        const trovatoDuplicato = await mytable
          .findOne({
            idapp: mytablerec.idapp,
            descr: mytablerec.descr,
            idCity: mytablerec.idCity,
            userId: mytablerec.userId,
          })
          .lean();
        if (trovatoDuplicato) {
          // trovatoDuplicato
          return res.status(200).send({ code: server_constants.RIS_CODE_REC_DUPLICATED_DESCR_CITY_USER, msg: '' });
        }
      }
    }

    return await myPromise
      .then(async (doupdate) => {
        if (false) {
          let plainObject = mytablerec.toObject();
          console.log(plainObject);
        }

        if (doupdate) return mytable.updateOne({ _id: mytablerec._id }, mydata, { new: true });
        else return mytablerec.save();
      })
      .then(async (risult) => {
        let rec = null;
        if (risult && risult.acknowledged) {
          rec = await mytable.findById(mytablerec._id).lean();
        } else {
          rec = risult;
        }

        if (shared_consts.TABLES_GETCOMPLETEREC.includes(params.table)) {
          return await mytablestrutt.getCompleteRecord(rec.idapp, rec._id);
        } else {
          return rec;
        }

        // tools.mylog('rec', rec);
      })
      .then(async (myrec) => {
        let setnotif = false;
        let typedir = 0;
        let typeid = 0;
        let groupnameDest = '';
        let circuitnameDest = '';

        if (isnewrec) {
          // New Record created

          if (shared_consts.TABLES_ADV_NOTIFICATION.includes(params.table)) {
            typedir = shared_consts.TypeNotifs.TYPEDIR_BACHECA;
            if (params.table === shared_consts.TABLES_MYGOODS) typeid = shared_consts.TypeNotifs.ID_BACHECA_NEW_GOOD;
            else if (params.table === shared_consts.TABLES_MYSKILLS)
              typeid = shared_consts.TypeNotifs.ID_BACHECA_NEW_SERVICE;
            else if (params.table === shared_consts.TABLES_MYHOSPS)
              typeid = shared_consts.TypeNotifs.ID_BACHECA_NEW_HOSP;
            setnotif = true;
          }

          if (shared_consts.TABLES_EVENTS_NOTIFICATION.includes(params.table)) {
            typedir = shared_consts.TypeNotifs.TYPEDIR_EVENTS;
            typeid = shared_consts.TypeNotifs.ID_EVENTS_NEW_REC;
            setnotif = true;
          }

          if (shared_consts.TABLES_GROUPS_NOTIFICATION.includes(params.table)) {
            typedir = shared_consts.TypeNotifs.TYPEDIR_GROUPS;
            typeid = shared_consts.TypeNotifs.ID_GROUP_NEW_REC;
            groupnameDest = myrec ? myrec.groupname : '';
            setnotif = true;
          }
          /*if (shared_consts.TABLES_CIRCUITS_NOTIFICATION.includes(params.table)) {
            typedir = shared_consts.TypeNotifs.TYPEDIR_CIRCUITS;
            typeid = shared_consts.TypeNotifs.ID_CIRCUIT_NEW_REC;
            circuitnameDest = myrec ? myrec.name : '';
            setnotif = (myrec.visibility === 0); // Not send a notification to others if the Circuit is HIDDEN or PRIVATE
          }*/
        }

        if (setnotif) {
          const myreq = { ...req };
          const myres = { ...res };
          SendNotif.createNewNotification(
            myreq,
            myres,
            { groupnameDest, circuitnameDest },
            params.table,
            myrec,
            typedir,
            typeid
          );
        }

        if (params.table === 'circuits') {
          await Circuit.updateData(myrec.idapp, myrec.name);
        }

        if (params.table === shared_consts.TAB_MYGROUPS && isnewrec) {
          // nuovo Record:
          // aggiungi il creatore al gruppo stesso
          return await User.setGroupsCmd(
            mydata.idapp,
            req.user.username,
            myrec.groupname,
            shared_consts.GROUPSCMD.SETGROUP,
            true,
            req.user.username
          ).then((ris) => {
            return res.send({ rec: myrec, ris });
          });
        } else if (params.table === shared_consts.TAB_MYCIRCUITS && isnewrec) {
          // nuovo Circuito:
          await User.setCircuitCmd(
            mydata.idapp,
            req.user.username,
            myrec.name,
            shared_consts.CIRCUITCMD.CREATE,
            true,
            req.user.username,
            extrarec
          ).then((ris) => {
            return res.send({ rec: myrec, ris });
          });

          // aggiungi il creatore al Circuito stesso
          return await User.setCircuitCmd(
            mydata.idapp,
            req.user.username,
            myrec.name,
            shared_consts.CIRCUITCMD.SET,
            true,
            req.user.username,
            extrarec
          ).then((ris) => {
            return res.send({ rec: myrec, ris });
          });
        }

        return res.send({ rec: myrec, ris: null });
      })
      .catch(async (e) => {
        console.error('settable', e.message);
        if (e.code === 11000) {
          const id = mytablerec._id;
          delete mytablerec._doc['_id'];
          const myfields = mytablerec._doc;
          if (!myfields.userId) {
            myfields.userId = req.user._id.toString();
          }
          return await mytablestrutt
            .findByIdAndUpdate(id, { $set: myfields })
            .then(async (rec) => {
              return res.send({ rec });
            })
            .catch((err) => {
              tools.mylog('error: ', err.message);
              return res.status(400).send(err);
            });
        } else {
          console.log(e.message);
        }
      });
  } catch (e) {
    console.error('settable', e.message);
    return res.status(400).send(e);
  }
});

router.post('/setsubrec', authenticate, (req, res) => {
  const params = req.body;
  const mytable = globalTables.getTableByTableName(params.table);
  const mydata = req.body.data;

  mydata.idapp = req.user.idapp;

  let mytablerec = new mytable(mydata);
  // console.log('mytablerec', mytablerec);

  const mytablestrutt = globalTables.getTableByTableName(params.table);

  const rec = mytablestrutt
    .createNewSubRecord(mydata.idapp, req)
    .then((rec) => {
      // tools.mylog('rec', rec);
      return res.send(rec);
    })
    .catch((e) => {});

  return res.send(rec);

  return mytablerec
    .save()
    .then((rec) => {
      // tools.mylog('rec', rec);
      return res.send(rec);
    })
    .catch((e) => {
      if (e.code === 11000) {
        const id = mytablerec._id;
        delete mytablerec._doc['_id'];
        const myfields = mytablerec._doc;
        if (!myfields.userId) {
          myfields.userId = req.user._id.toString();
        }
        return mytablestrutt
          .findByIdAndUpdate(id, { $set: myfields })
          .then(async (rec) => {
            return res.send(rec);
          })
          .catch((err) => {
            tools.mylog('error: ', err.message);
            return res.status(400).send(err);
          });
      } else {
        console.log(e.message);
      }
    });
});

router.post('/getobj', authenticate_noerror, async (req, res) => {
  try {
    let cmd = req.body.cmd;
    let idapp = req.user ? req.user.idapp : sanitizeHtml(req.body.idapp); // Cambiato from params.idapp a req.body.idapp
    let ris = null;

    if (cmd === 'lista_editori') {
      ris = await User.find(
        {
          idapp,
          perm: { $bitsAnySet: 0b10000 },
        },
        { username: 1, name: 1, surname: 1 }
      )
        .sort({ username: 1 })
        .lean();
    } else if (cmd === 'lista_referenti') {
      ris = await User.find(
        {
          idapp,
          perm: { $bitsAnySet: 0b110010000 },
        },
        { username: 1, name: 1, surname: 1 }
      )
        .sort({ username: 1 })
        .lean();
    }

    // Invia la risposta
    return res.status(200).send({ code: server_constants.RIS_CODE_OK, data: ris });
  } catch (e) {
    console.error(`ERROR getobj`, e.message);
    res.status(200).send({ code: server_constants.RIS_CODE_OK, data: [] });
  }
});

router.post('/gettable', authenticate_noerror, (req, res) => {
  try {
    let params = req.body;

    params.table = sanitizeHtml(params.table);

    if (!shared_consts.TABLES_ENABLE_GETTABLE_FOR_NOT_LOGGED.includes(params.table) && !req.user) {
      return res
        .status(server_constants.RIS_CODE_HTTP_FORBIDDEN_PERMESSI)
        .send({ msgerr: 'Non hai i permessi per vedere questa pagina.' });
    }

    let idapp = req.user ? req.user.idapp : sanitizeHtml(params.idapp);
    const mytable = globalTables.getTableByTableName(params.table);
    //console.log('mytable', mytable);
    if (!mytable) {
      console.log(`Table ${params.table} not found`);
      return res.status(400).send({});
    }

    if (req.user && req.user.username) {
      User.setOnLine(req.user.idapp, req.user.username);
    }

    return mytable
      .executeQueryTable(idapp, params, req.user)
      .then((ris) => {
        // console.log('ris=', ris);
        return res.send(ris);
      })
      .catch((e) => {
        console.error('gettable: ' + e.message);
        res.status(400).send(e);
      });
  } catch (e) {
    console.error(`ERROR gettable ${params.table}: `, e.message, 'params', params);
    res.status(500).send(e);
  }
});

router.post('/getexp', authenticate, (req, res) => {
  const params = req.body;
  let idapp = req.user.idapp;
  const myUser = globalTables.getTableByTableName('users');
  // console.log('mytable', mytable);
  if (!myUser || params.filtersearch2 !== 'fdsgas1') {
    return res.status(400).send({});
  }

  if (!User.isAdmin(req.user.perm) && !User.isManager(req.user.perm) && !User.isFacilitatore(req.user.perm)) {
    // If without permissions, exit
    return res
      .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
      .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
  }

  try {
    if (params.table === 'exp') {
      return myUser
        .find(
          {
            idapp,
            $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
          },
          {
            username: 1,
            name: 1,
            surname: 1,
            email: 1,
            reported: 1,
            date_report: 1,
            username_who_report: 1,
            'profile.teleg_id': 1,
            verified_by_aportador: 1,
            'profile.username_telegram': 1,
            'profile.firstname_telegram': 1,
            'profile.lastname_telegram': 1,
          }
        )
        .then((ris) => {
          return res.send(ris);
        })
        .catch((e) => {
          console.error('getexp: ' + e.message);
          res.status(400).send(e);
        });
    }
  } catch (e) {
    console.error(`ERROR getexp ${params.table}: `, e.message, 'params', params);
    res.status(500).send(e);
  }
});

router.post('/pickup', authenticate_noerror, (req, res) => {
  try {
    const params = req.body;
    let idapp = req.body.idapp;
    let mytable = globalTables.getTableByTableName(params.table);
    // console.log('mytable', mytable);
    if (!mytable) {
      console.log(`Table ${params.table} not found`);
      return res.status(400).send({});
    }

    return mytable
      .executeQueryPickup(idapp, params)
      .then((ris) => {
        return res.send(ris);
      })
      .catch((e) => {
        console.log(e.message);
        res.status(400).send(e);
      });
  } catch (e) {
    res.status(400).send(e);
  }
});

router.post('/getpage', async (req, res) => {
  const params = req.body;
  const idapp = req.body.idapp;
  const mypath = params.path;

  let found = await MyPage.findOne({ idapp, path: mypath })
    .then((ris) => {
      if (ris && ris._doc) return res.send({ mypage: ris._doc });
      else return null;
    })
    .catch((e) => {
      console.log(e.message);
      res.status(400).send(e);
    });

  if (!found) {
    // trova quelli con il :
    let regexp = new RegExp(`:`, 'ig');
    const searchpagesSpec = await MyPage.find({ idapp, path: { $regex: regexp } });

    if (searchpagesSpec) {
      let arrsubpath = mypath.split('/');
      for (let i = 0; i < searchpagesSpec.length; i++) {
        let arrsubstr = searchpagesSpec[i].path.split('/');

        if (arrsubpath.length === arrsubpath.length) {
          let mypathbuild = '';

          for (let j = 0; j < arrsubstr.length; j++) {
            if (arrsubstr[j].includes(':')) {
              mypathbuild += arrsubpath[j] + '/';
            } else {
              mypathbuild += arrsubstr[j] + '/';
            }
          }

          if (mypath + '/' === mypathbuild) {
            return res.send({ mypage: searchpagesSpec[i] });
          }
        }
      }
    }

    return await MyPage.findOne({ idapp, path: mypath }).then((ris) => {
      return res.send({ mypage: ris });
    });
  }
  return found;
});

router.post('/savepage', authenticate, async (req, res) => {
  const params = req.body;
  const idapp = req.body.idapp;
  const mypage = params.page;

  try {
    if (!mypage?._id) {
      // creazione nuovo record
      return await MyPage.create({ idapp, ...mypage })
        .then((ris) => {
          if (ris) {
            return res.send({ code: server_constants.RIS_CODE_OK, mypage: ris });
          }
          return res.send({ code: server_constants.RIS_CODE_ERR, msg: '' });
        })
        .catch((e) => {
          console.log(e.message);
          res.status(400).send(e);
        });
    } else {
      // update record
      return await MyPage.findOneAndUpdate({ idapp, _id: mypage._id }, mypage, { upsert: true, new: true })
        .then((ris) => {
          if (ris) {
            return res.send({ code: server_constants.RIS_CODE_OK, mypage: ris });
          }
          return res.send({ code: server_constants.RIS_CODE_ERR, msg: '' });
        })
        .catch((e) => {
          console.log(e.message);
          res.status(400).send(e);
        });
    }
  } catch (e) {
    console.error('Error', e);
  }
});

async function exportPage(idapp, pageId) {
  try {
    const myexp = {
      mypages: [],
      myelems: [],
    };

    // Trova il record di Page da duplicare
    const pageToExp = await MyPage.find({ _id: pageId, idapp }).lean();
    if (!pageToExp) {
      console.error('Page not found.');
      return;
    }

    myexp.mypages = [...pageToExp];

    // Trova tutti gli elementi associati a Page da duplicare
    const elemsToExp = await MyElem.find({ idapp, idPage: pageId }).lean();

    myexp.myelems = [...elemsToExp];

    const jsonString = JSON.stringify(myexp);

    if (jsonString) {
      console.log('Esportazione completata con successo.');

      return jsonString;
    }

    return '';
  } catch (error) {
    console.error("Errore durante l'esportazione:", error);
    return '';
  }
}

async function upsertRecord(table, record, appId, newIdPage = null) {
  let newId = null;

  const existingRecord = await table.findOne({ idapp: appId, _id: record._id }); // Assumendo che `record` ha un ID
  if (existingRecord) {
    if (newIdPage && record.idPage) {
      record.idPage = newIdPage;
    }
    const modif = await table.updateOne({ _id: record._id }, { $set: { ...record, idapp: appId } });
    wasModified = modif.modifiedCount > 0;
  } else {
    // Se sono sulla tabella mypages
    if (table.modelName === 'MyPage') {
      // Controlla se esiste già la pagina in questione
      const existingRecPage = await table.findOne({ idapp: appId, path: record.path });
      if (existingRecPage) {
        // Esiste già ! quindi aggiorno questo record, e cancello tutti gli elementi precedenti !
        delete record._id;
        const modif = await table.updateOne({ _id: existingRecPage._id }, { $set: { ...record, idapp: appId } });

        const tableElems = globalTables.getTableByTableName('myelems');
        if (tableElems) risdel = await tableElems.deleteMany({ idapp: appId, idPage: existingRecPage._id });

        newId = existingRecPage._id.toString();
        return { ImportedRecords: false, newId }; // Torna il numero di record importati (1 in questo caso)
      }
    }
    // Se non esiste, allora la creo, con un id Nuovo !

    const existingRecSameID = await table.findOne({ _id: record._id }); // Assumendo che `record` ha un ID
    if (existingRecSameID) {
      // Se
      newId = new ObjectId();
      record._id = newId;
    }
    if (newIdPage && record.idPage) {
      record.idPage = newIdPage;
    }
    const newrec = {
      ...record,
      idapp: appId,
    };

    const ris = await table.create(newrec);
    wasModified = !!ris;
  }

  return { ImportedRecords: wasModified, newId }; // Torna il numero di record importati (1 in questo caso)
}

async function importPage(req, idapp, jsonString) {
  try {
    console.log('INIZIO importPage');

    // Parsing dei dati JSON
    const myexp = JSON.parse(jsonString);

    // Assicurati che i dati siano ben strutturati
    if (!myexp) {
      console.error("Dati non validi per l'importazione.");
      return;
    }

    let totalImportedRecords = 0;
    let newIdPage = null;

    const importCounts = []; // Array per memorizzare i conteggi di importazione

    // Cicla prima sulle pagine
    for (const key in myexp) {
      if (myexp.hasOwnProperty(key)) {
        let ImportedRecordstemp = 0;
        const tableName = key;

        // Verifica se la tabella esiste
        if (tableName) {
          const table = globalTables.getTableByTableName(tableName);

          if (tableName === 'mypages') {
            if (
              User.isAdmin(req.user.perm) ||
              User.isEditor(req.user.perm) ||
              User.isCommerciale(req.user.perm) ||
              User.isCollaboratore(req.user.perm)
            ) {
              for (const page of myexp.mypages) {
                const { ImportedRecords, newId } = await upsertRecord(table, page, idapp);
                if (!newIdPage && newId) {
                  newIdPage = newId;
                }
                ImportedRecordstemp += ImportedRecords ? 1 : 0;
              }
            }
          }
          totalImportedRecords += ImportedRecordstemp;
        }
        if (ImportedRecordstemp > 0) importCounts.push({ tableName, count: ImportedRecordstemp });
      }
    }

    // Ciclo su ogni proprietà di myexp
    for (const key in myexp) {
      if (myexp.hasOwnProperty(key)) {
        let ImportedRecordstemp = 0;
        const tableName = key;

        // Verifica se la tabella esiste
        if (tableName) {
          const table = globalTables.getTableByTableName(tableName);

          if (tableName === 'myelems') {
            if (User.isEditor(req.user.perm) || User.isCommerciale(req.user.perm)) {
              for (const elem of myexp.myelems) {
                const { ImportedRecords, newId } = await upsertRecord(table, elem, idapp, newIdPage);
                ImportedRecordstemp += ImportedRecords ? 1 : 0;
              }
            }
          } else if (tableName === 'myusers') {
            if (User.isManager(req.user.perm)) {
              for (const user of myexp.myusers) {
                const { ImportedRecords, newId } = await upsertRecord(table, user, idapp);
                ImportedRecordstemp += ImportedRecords ? 1 : 0;
              }
            }
          } else if (table && tableName !== 'mypages') {
            // Tutte le altre tabelle
            if (User.isManager(req.user.perm)) {
              for (const rec of myexp[key]) {
                const { ImportedRecords, newId } = await upsertRecord(table, rec, idapp);
                ImportedRecordstemp += ImportedRecords ? 1 : 0;
              }
            }
          }

          totalImportedRecords += ImportedRecordstemp;
          if (ImportedRecordstemp > 0) importCounts.push({ tableName, count: ImportedRecordstemp });
        }
      }
    }

    if (totalImportedRecords) {
      console.log(`Importazione completata con successo. Totale record importati: ${totalImportedRecords}`);
    }

    return { imported: totalImportedRecords, importCounts };
  } catch (error) {
    console.error("Errore durante l'importazione:", error);
  }
}

router.post('/duppage', authenticate, async (req, res) => {
  const params = req.body;
  const idapp = req.body.idapp;
  const mypath = params.path;
  const newpath = params.newpath;

  try {
    let found = await MyPage.findOne({ idapp, path: mypath })
      .then(async (ris) => {
        const result = await globalTables.duplicatePage(ris._id, newpath);
        if (result && result.newpage) {
          return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
        } else {
          return res.send({ code: server_constants.RIS_CODE_ERR, msg: '' });
        }
      })
      .catch((e) => {
        console.log(e.message);
        res.status(400).send(e);
      });
  } catch (e) {
    console.error('Error', e);
  }
});

router.post('/exppage', authenticate, async (req, res) => {
  const params = req.body;
  const idapp = req.body.idapp;
  const mypath = params.path;

  try {
    let found = await MyPage.findOne({ idapp, path: mypath })
      .then(async (ris) => {
        const resultJSon = await exportPage(idapp, ris._id);
        if (resultJSon) {
          return res.send({ code: server_constants.RIS_CODE_OK, json: resultJSon });
        } else {
          return res.send({ code: server_constants.RIS_CODE_ERR, msg: '' });
        }
      })
      .catch((e) => {
        console.log(e.message);
        res.status(400).send(e);
      });
  } catch (e) {
    console.error('Error', e);
  }
});
router.post('/imppage', authenticate, async (req, res) => {
  const params = req.body;
  const idapp = req.body.idapp;
  const jsonString = params.jsonString;

  try {
    const result = await importPage(req, idapp, jsonString);
    if (result) {
      return res.send({ code: server_constants.RIS_CODE_OK, ris: result });
    } else {
      return res.send({ code: server_constants.RIS_CODE_ERR, ris: '' });
    }
  } catch (e) {
    console.log(e.message);
    res.status(400).send(e);
  }
});

router.patch('/setlang', authenticate, async (req, res) => {
  const username = req.body.data.username;
  const idapp = req.user.idapp;
  const mydata = req.body.data;

  const lang = mydata.lang;

  const fieldsvalue = {
    lang,
  };

  if (!!lang) {
    const rec = await User.findByUsername(idapp, username, false);
    let ris = null;
    if (!!rec) ris = await User.findByIdAndUpdate(rec.id, { $set: fieldsvalue });

    if (!!ris) {
      return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
    }

    res.status(400).send();
  }
});

function toObjId(id) {
  try {
    return Types.ObjectId.isValid(id) ? new Types.ObjectId(id) : id;
  } catch {
    return id;
  }
}

async function updateElemInsideColumn({ idapp, id, mydata }) {
  const { fieldsvalue = {} } = mydata || {};

  // 1) id della colonna parent (dove si trova l'elemento da aggiornare)
  let idElemParent = null;
  if (fieldsvalue && fieldsvalue.idElemParent) {
    idElemParent = fieldsvalue.idElemParent;
  }

  // 2) recupera il documento parent "top-level" (es. section) che contiene la colonna
  const myelemParent = await MyElem.findParentElem(idapp, idElemParent);
  if (!myelemParent) {
    return { ok: false, msg: 'Parent non trovato' };
  }

  // 3) costruisci il $set campo-per-campo
  //    path target: rows.$[].columns.$[col].elems.$[el].<campo>
  const setOps = {};
  for (const [key, value] of Object.entries(fieldsvalue)) {
    if (key === '_id') continue; // non tocchiamo l'_id
    setOps[`rows.$[].columns.$[col].elems.$[el].${key}`] = value;
  }

  // Se non c’è nulla da settare, esci pulito
  if (Object.keys(setOps).length === 0) {
    return { ok: true, msg: 'Nulla da aggiornare' };
  }

  // 4) esegui l’update
  const filter = { _id: toObjId(myelemParent._id) };
  const update = { $set: setOps };
  const options = {
    arrayFilters: [{ 'col._id': toObjId(idElemParent) }, { 'el._id': toObjId(id) }],
  };

  const result = await MyElem.updateOne(filter, update, options);

  return {
    ok: result?.matchedCount > 0,
    modified: result?.modifiedCount || 0,
    result,
  };
}

router.patch('/chval', authenticate, async (req, res) => {
  // const idapp = req.body.idapp;
  const id = req.body.data.id;
  const idapp = req.user.idapp;
  const mydata = req.body.data;

  try {
    const mytable = globalTables.getTableByTableName(mydata.table);
    let fieldsvalue = mydata.fieldsvalue;
    const unset = mydata.unset;

    const { Account } = require('../models/account');

    // tools.mylogshow('PATCH CHVAL: ', id, fieldsvalue);

    // If I change my record...
    if (
      !User.isAdmin(req.user.perm) &&
      !User.isManager(req.user.perm) &&
      !User.isEditor(req.user.perm) &&
      !User.isCommerciale(req.user.perm) &&
      !User.isFacilitatore(req.user.perm) &&
      (await !tools.ModificheConsentite(req, mydata.table, fieldsvalue, id)) &&
      !(mydata.table === 'accounts' && (await Account.canEditAccountAdmins(req.user.username, mydata.id)))
    ) {
      // If without permissions, exit
      return res
        .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
        .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
    }

    const camporequisiti = UserCost.FIELDS_REQUISITI.includes(Object.keys(fieldsvalue)[0]);

    let allData = {};
    let username = '';
    if (mydata.table === 'users') {
      if (camporequisiti) {
        allData = {};
        allData.myuser = await User.getUserById(idapp, id);
        username = allData.myuser.username;
        if (!!allData.myuser) allData.precDataUser = await User.getInfoUser(idapp, allData.myuser.username);
        else allData.precDataUser = null;
        // allData.useraportador = await ListaIngresso.getUserByInvitante_Username(idapp, allData.myuser.aportador_solidario);
        // allData.precDataAportador = await getInfoUser(idapp, allData.myuser.aportador_solidario);
      }
    }

    let index = 0;

    let recoldnave = null;

    let myuser = null;
    let mydatamsg = {};

    let flotta = null;
    let strflotta = '';

    let profileData = {};

    if (shared_consts.TABLES_UPDATE_LASTMODIFIED.includes(mydata.table)) {
      fieldsvalue.date_updated = new Date();
    }

    const numobj = tools.getNumObj(fieldsvalue);
    if (numobj === 1 && fieldsvalue['_id']) {
      const myrec = await mytable.findById(id);
      const myidDel = myrec['_id'];
      myrec['_id'] = fieldsvalue['_id'];
      return await mytable.insertMany(myrec).then((ris) => {
        if (ris) {
          return mytable.deleteMany({ _id: myidDel }).then((risdel) => {
            return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
          });
        } else {
          return res.status(404).send();
        }
      });
    }

    const updatebot = UserCost.FIELDS_UPDATE_TELEGRAM_BOT.includes(Object.keys(fieldsvalue)[0]);

    tools.refreshAllTablesInMem(idapp, mydata.table, updatebot, username);

    if (mydata.table === shared_consts.TAB_SETTINGS) {
      if (shared_consts.KEY_TO_CRYPTED.includes(fieldsvalue.key)) {
        fieldsvalue.crypted = true;
        fieldsvalue.value_str = tools.cryptdata(fieldsvalue.value_str);
      }
    }

    if (mydata.table === shared_consts.TAB_SITES) {
      if (shared_consts.SITES_KEY_TO_CRYPTED in fieldsvalue) {
        fieldsvalue[shared_consts.SITES_KEY_TO_CRYPTED] = tools.cryptdata(
          fieldsvalue[shared_consts.SITES_KEY_TO_CRYPTED]
        );
      }
    }

    let precRec = null;

    if (mydata.table === 'accounts') {
      precRec = await mytable.findById(id);
    }

    if (mydata.table === 'arrvariazioni') {
      let chiave = null;
      let valore = null;

      for (const [key, value] of Object.entries(fieldsvalue)) {
        chiave = key;
        valore = value;
      }

      if (chiave) {
        // Costruiamo la stringa di assegnazione dinamica
        fieldsvalue = { [`arrvariazioni.0.${chiave}`]: valore };
      }
    }
    if (mydata.table === 'myelems' && mydata.fieldsvalue.idElemParent) {
      // se è un myelem, allora cerca l'id anche sugli elementi contenuti in elems, (sotto rows e columns)
      // quindi devo aggiornare quell'elemento in elems

      const risult = await updateElemInsideColumn({ idapp, id, mydata });

      if (risult.ok) {
        res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
      }

      /*return await mytable.findByIdAndUpdate(id, { $set: fieldsvalue }, { new: true }).then(async (rec) => {
        res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
      });*/
    } else {
      return await mytable
        .findByIdAndUpdate(id, { $set: fieldsvalue }, { new: true })
        .then(async (rec) => {
          // tools.mylogshow(' REC TO MODIFY: ', rec);
          if (!rec) {
            return res.status(404).send();
          } else {
            let addmsg = '';

            if (mydata.notifBot) {
              // Send Notification to the BOT
              await telegrambot.sendMsgTelegram(idapp, mydata.notifBot.un, mydata.notifBot.txt);
              if (!!addmsg) await telegrambot.sendMsgTelegram(idapp, mydata.notifBot.un, addmsg);
              let addtext = '[Msg Inviato a ' + mydata.notifBot.un + ']:' + '\n' + mydata.notifBot.txt;
              telegrambot.sendMsgTelegramToTheManagers(idapp, addtext, true);

              if (!!flotta) tools.writeFlottaLog(idapp, addtext, flotta.riga, flotta.col_prima);
            }

            if (mydata.table === 'accounts') {
              let msg = '';
              if (rec.circuitId) circuit = await Circuit.getCircuitByCircuitId(rec.circuitId);

              let dest = rec.groupname ? rec.groupname : rec.username;
              let valprec = 0;

              if ('saldo' in fieldsvalue) {
                valprec = precRec && precRec.saldo ? precRec.saldo : 0;
                msg = i18n.__(
                  'SALDO_VARIATO',
                  circuit.name,
                  req.user.username,
                  dest,
                  valprec,
                  fieldsvalue.saldo,
                  circuit.symbol
                );
              } else if ('fidoConcesso' in fieldsvalue) {
                valprec = precRec && precRec.fidoConcesso ? precRec.fidoConcesso : 0;
                msg = i18n.__(
                  'FIDOCONCESSO_VARIATO',
                  circuit.name,
                  req.user.username,
                  dest,
                  valprec,
                  fieldsvalue.fidoConcesso,
                  circuit.symbol
                );
              } else if ('qta_maxConcessa' in fieldsvalue) {
                valprec = precRec && precRec.qta_maxConcessa ? precRec.qta_maxConcessa : 0;
                msg = i18n.__(
                  'QTAMAX_VARIATO',
                  circuit.name,
                  req.user.username,
                  dest,
                  valprec,
                  fieldsvalue.qta_maxConcessa,
                  circuit.symbol
                );
              }

              if (msg) {
                telegrambot.sendMsgTelegramToTheManagers(idapp, msg);
                telegrambot.sendMsgTelegramToTheAdminsOfCircuit(idapp, circuit.path, msg);
              }
            }

            if (mydata.table === 'users') {
              let provincia = '';

              if ('profile.resid_comune' in fieldsvalue) {
                const idcomune = fieldsvalue['profile.resid_comune'];

                profileData = await User.updateProvinceUserByComune(idapp, id, idcomune);

                provincia = profileData.profile.resid_province;
              } else {
                provincia = rec.profile.resid_province;
              }

              if (provincia) {
                const card = fieldsvalue.hasOwnProperty('profile.resid_card') ? fieldsvalue['profile.resid_card'] : '';
                // Controlla se esiste il Circuito di questa provincia, se non esiste lo crea!
                await Circuit.createCircuitIfNotExist(req, idapp, provincia, card);
              }

              if (camporequisiti) {
                await User.checkIfSbloccatiRequisiti(idapp, allData, id);
              }

              if ('aportador_solidario' in fieldsvalue) {
                let ind_order_ingr = mydata.ind_order_ingr;
                // SERVE SE CI METTO LE MINUSCOLE/MAIUSCOLE SBAGLIATE in invitante_username!
                const myuserfound = await User.findByUsername(idapp, fieldsvalue.aportador_solidario, false);
                if (!!myuserfound) {
                  if (!!myuserfound._id && !myuserfound.deleted) {
                    const aportador = await User.getUsernameById(idapp, myuserfound._id);
                    fieldsvalue.aportador_solidario = aportador;
                    //Aggiorna record !
                    await mytable.findByIdAndUpdate(id, { $set: fieldsvalue });
                  }
                } else {
                  res.send({
                    code: server_constants.RIS_CODE_ERR,
                    msg: 'Non aggiornato',
                  });
                  res.status(400).send();
                  return false;
                }
              } else if ('deleted' in fieldsvalue) {
                let msg = '';
                if (fieldsvalue.deleted) msg = 'cancellato (nascosto)';
                else msg = 'Ripristinato';

                await telegrambot.sendMsgTelegramToTheManagers(
                  idapp,
                  `L\'utente ` +
                    tools.getNomeCognomeEUserNameByUser(rec) +
                    ` è stato ${msg} da ` +
                    tools.getNomeCognomeEUserNameByUser(req.user)
                );
              }
            }

            if (await tools.ModificheConsentite(req, mydata.table, fieldsvalue)) {
              let msg = '';
              if (mydata.table === 'users') {
                if ('aportador_solidario' in fieldsvalue) {
                  const nomecognomenuovo = await User.getNameSurnameByUsername(idapp, fieldsvalue.aportador_solidario);
                  const nomecognomeas = await User.getNameSurnameByUsername(idapp, rec.aportador_solidario);
                  msg =
                    `Variato l'invitante di ` +
                    tools.getNomeCognomeEUserNameByUser(rec) +
                    '\nmodificato da ' +
                    tools.getNomeCognomeEUserNameByUser(req.user) +
                    ' \n' +
                    'Prima: ' +
                    nomecognomeas +
                    ' (' +
                    rec.aportador_solidario +
                    ')\n' +
                    'Dopo: ' +
                    nomecognomenuovo +
                    ' (' +
                    fieldsvalue.aportador_solidario +
                    ') ]';

                  // Metti l'iniziale
                  if (!(await User.AportadorOrig(id))) {
                    await mytable.findByIdAndUpdate(
                      id,
                      { $set: { aportador_iniziale: fieldsvalue.aportador_solidario } },
                      { new: false }
                    );
                  }
                }
              }

              if (msg !== '') telegrambot.sendMsgTelegramToTheManagers(idapp, msg);
            }

            res.send({ code: server_constants.RIS_CODE_OK, msg: '', profileData });
          }
        })
        .catch((e) => {
          tools.mylogserr('Error patch USER: ', e.message);
          res.status(400).send();
        });
    }
  } catch (e) {
    tools.mylogserr('Error chval: ', e.message);
    res.status(400).send();
  }
});

router.patch('/askfunz', authenticate, async (req, res) => {
  // const idapp = req.body.idapp;
  const id = req.body.data.id;
  // const ind_order = req.body.data.ind_order;
  // const username = req.body.data.username;
  const idapp = req.user.idapp;
  const mydata = req.body.data;

  let entra = false;
  if (!entra) {
    // If I change my record...
    if (
      !User.isAdmin(req.user.perm) &&
      !User.isManager(req.user.perm) &&
      !User.isFacilitatore(req.user.perm) &&
      req.user._id.toString() !== id
    ) {
      // If without permissions, exit
      return res
        .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
        .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
    }
  }

  if (mydata.myfunc === shared_consts.CallFunz.DAMMI_PRIMO_UTENTE_LIBERO) {
    const userfree = await Graduatoria.getFirstUserGradFree(idapp);

    if (!!userfree) return res.send({ code: server_constants.RIS_CODE_OK, out: userfree });
    /*} else if (mydata.myfunc === shared_consts.CallFunz.GET_VALBYTABLE) {
      const mytable = globalTables.getTableByTableName(mydata.table);
      const coltoshow = {
        [mydata.coltoshow]: 1
      };
 
      const ris = await mytable.findOne({ _id: id }, coltoshow);
 
      return ris;
    } else if (mydata.myfunc === shared_consts.CallFunz.SET_VALBYTABLE) {
      const mytable = globalTables.getTableByTableName(mydata.table);
      const value = mydata.value;
      const coltoset = {
        [mydata.coltoshow]: value
      };
 
      const ris = await mytable.findOneAndUpdate({ _id: id }, { $set: coltoset }, { new: false });
      if (!!ris)
        return res.send({ code: server_constants.RIS_CODE_OK });*/
  }

  return res.send({ code: server_constants.RIS_CODE_ERR });
});

router.patch('/callfunz', authenticate, async (req, res) => {
  // const idapp = req.body.idapp;
  const id = req.body.data.id;
  // const ind_order = req.body.data.ind_order;
  const username = req.body.data.username;
  const idapp = req.user.idapp;
  const mydata = req.body.data;

  // const telegrambot = require('../telegram/telegrambot');

  try {
    let entra = false;
    if (
      mydata.myfunc === shared_consts.CallFunz.AGGIUNGI_NUOVO_IMBARCO ||
      mydata.myfunc === shared_consts.CallFunz.CANCELLA_IMBARCO
    ) {
      entra = true;
    }
    if (!entra) {
      // If I change my record...
      if (
        !User.isAdmin(req.user.perm) &&
        !User.isManager(req.user.perm) &&
        !User.isFacilitatore(req.user.perm) &&
        req.user._id.toString() !== id
      ) {
        // If without permissions, exit
        return res
          .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
          .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
      }
    }

    let myuser = await User.findOne({ idapp, username });

    let fieldsvalue = {};

    if (mydata.myfunc === shared_consts.CallFunz.ZOOM_GIA_PARTECIPATO) {
      if (!!myuser.username) {
        let FormDaMostrare = telegrambot.getFormDaMostrare(idapp, mydata.myfunc, myuser);

        await telegrambot.sendMsgTelegramToTheManagers(
          idapp,
          `L\'utente ${myuser.name} ${myuser.surname} (${myuser.username}) ha detto di aver già visto lo Zoom di Benvenuto`,
          false,
          FormDaMostrare
        );

        const ris = await User.findByIdAndUpdate(myuser.id, { $set: { 'profile.ask_zoom_partecipato': true } });
        if (ris) return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
      }
    }

    return res.send({ code: server_constants.RIS_CODE_ERR });
  } catch (e) {
    console.log(e.message);
    res.status(400).send();
  }
});

router.get('/copyfromapptoapp/:idapporig/:idappdest', async (req, res) => {
  const idapporig = req.params.idapporig;
  const idappdest = req.params.idappdest;
  const idcode = req.params.code;
  if (!idapporig || !idappdest || idcode !== 'ASD3429Kjgà#@cvX') res.status(400).send();

  const mytablesstr = ['settings', 'users', 'templemail', 'destnewsletter'];

  try {
    let numrectot = 0;
    for (const table of mytablesstr) {
      const mytable = globalTables.getTableByTableName(table);

      tools.mylogshow('copyfromapptoapp: ', table, mytable);

      await mytable.DuplicateAllRecords(idapporig, idappdest).then((numrec) => {
        // tools.mylogshow(' REC TO MODIFY: ', rec);
        numrectot += numrec;
      });
    }

    res.send({ code: server_constants.RIS_CODE_OK, msg: '', numrectot });
  } catch (e) {
    tools.mylogserr('Error copyfromapptoapp: ', e);
    res.status(400).send();
  }
});

router.delete('/delrec/:table/:id', authenticate, async (req, res) => {
  const id = req.params.id;
  // const idapp = req.user.idapp;
  const tablename = req.params.table;
  // let notifBot = false;
  // const idapp = req.body.idapp;

  console.log('delete RECORD: id', id, 'table', tablename);

  const mytable = globalTables.getTableByTableName(tablename);

  const fields = { ALL: 1 };

  if (
    !User.isAdmin(req.user.perm) &&
    !User.isManager(req.user.perm) &&
    tablename !== 'extralist' &&
    (await !tools.ModificheConsentite(req, tablename, fields, id, req.user))
  ) {
    // If without permissions, exit
    return res
      .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
      .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
  }

  let cancellato = false;

  //++Tools: Notify...
  tools.NotifyIfDelRecord(tablename);

  // if (!User.isAdmin(req.user.perm) && !User.isManager(req.user.perm)) {
  if (true) {
    if (tablename === 'users') {
      let fieldsvalue = {
        deleted: true,
        date_deleted: new Date(),
      };
      const utente = await mytable.findById(id);

      if (utente) {
        idapp = utente.idapp;
        await mytable.findByIdAndUpdate(id, { $set: fieldsvalue });

        // ...
        let text =
          `L\'utente ${utente.username} (${utente.name} ${utente.surname}) si è cancellato dal sito ` +
          tools.getNomeAppByIdApp(utente.idapp) +
          ` Deleted = true`;
        telegrambot.sendMsgTelegramToTheManagers(idapp, text);
      }

      if (!User.isAdmin(req.user.perm)) {
        cancellato = true;
        return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
      }
    }
  }

  if (tablename === 'catalogs') {
    // Devo cancellare anche la pagina associato al Catalogo!

    const myrec = await mytable.findOne({ _id: id }).lean();
    if (myrec.idPageAssigned) {
      await MyPage.deleteOne({ _id: myrec.idPageAssigned }).then((rec) => {
        if (!rec) {
          console.log('Errore cancellazione pagina associata al catalogo');
        }
      });
    }
  }

  let ris = null;

  if (!cancellato) {
    // ELIMINA VERAMENTE IL RECORD !!!
    ris = await mytable
      .deleteOne({ _id: id })
      .then((rec) => {
        if (!rec) {
          // res.status(404).send();
          return false;
        }

        if (tablename === shared_consts.TAB_MYGROUPS) {
          // Se è un gruppo, allora cancella anche tutti i suoi riferimenti
          User.removeAllUsersFromMyGroups(rec.idapp, rec.groupname);
        } else if (tablename === shared_consts.TAB_MYCIRCUITS) {
          // Se è un gruppo, allora cancella anche tutti i suoi riferimenti
          User.removeAllUsersFromMyCircuits(rec.idapp, rec.name);
        } else if (tablename === shared_consts.TAB_MYPAGES) {
          // Cancella tutti gli elementi di quella pagina
          MyElem.deleteAllFromThisPage(rec._id);
        }

        tools.refreshAllTablesInMem(rec.idapp, tablename, true, rec.username);

        cancellato = true;

        tools.mylog('DELETED ', rec._id);

        return true;
      })
      .catch((e) => {
        console.log(e.message);
        res.status(400).send();
      });
  }

  if (cancellato) {
    // Do extra things after deleted
    //let ris2 = await actions.doOtherlasThingsAfterDeleted(tablename, myrec, notifBot, req);
    if (!!ris) {
      return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
    }
  }

  res.send({ code: server_constants.RIS_CODE_ERR, msg: '' });
  return ris;
});

router.post('/duprec/:table/:id', authenticate, async (req, res) => {
  const id = req.params.id;
  const tablename = req.params.table;
  // const idapp = req.body.idapp;

  console.log('id', id, 'table', tablename);

  const mytable = globalTables.getTableByTableName(tablename);

  if (!req.user) {
    return res
      .status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
      .send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
  }

  /* if (!User.isAdmin(req.user.perm) && !User.isManager(req.user.perm)) {
    // If without permissions, exit
    return res.status(404).
      send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
  } */

  return await mytable.findById(id).then(async (mydata) => {
    const datadup = tools.CloneRecordToNew(mydata, mytable.modelName);
    const mynewrec = new mytable(datadup);

    return await mynewrec
      .save()
      .then(async (rec) => {
        if (!rec) {
          return res.status(404).send();
        }

        tools.mylog('DUPLICATED ', rec);

        // Do extra things after deleted
        return await actions.doOtherThingsAfterDuplicated(tablename, rec).then(({ myrec }) => {
          // ...
          mytable.findById(myrec._id).then((record) => {
            return res.send({ code: server_constants.RIS_CODE_OK, record, msg: '' });
          });
        });
      })
      .catch((e) => {
        console.error(e.message);
        res.status(400).send();
      });
  });
});

router.get('/loadsite/:userId/:idapp', authenticate_noerror_WithUserLean, async (req, res) => {
  try {
    // if ((req.statuscode2 = server_constants.RIS_CODE_HTTP_TOKEN_EXPIRED)) {
    //   return;
    // }

    await load(req, res, '0');
  } catch (e) {
    console.error('loadsite error', e);
    res.status(500).send({ error: 'Impossibile caricare il sito' });
  }
});

// Funzione di test per misurare le performance di MongoDB
async function testMongoPerformance(ind, iterations = 20) {
  let logString = '';
  const log = (msg) => {
    logString += msg + '\n';
  };

  log(`Avvio del test ${ind} di performance MongoDB con ${iterations} iterazioni...`);

  const timings = [];

  for (let i = 0; i < iterations; i++) {
    const start = process.hrtime();
    try {
      // Esegui una query semplice; sostituisci "User" con il tuo modello se necessario
      if (ind === 1) {
        await User.findOne({}).lean();
      } else {
        const token =
          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiJQUk9WQU1TR0AxQSIsInNtYXJ0IjoiNjIwODAwYWRjMTI5ZDFlYmE3NjBiZWNiIiwiYWNjZXNzIjoiYXV0aCIsInVuIjoic3VyeWExOTc3IiwiaWF0IjoxNzQxODcyMzEwLCJleHAiOjE3NDE4Nzk1MTB9.SXJLmsS6EZVhaU7sUWYMnaqGpiiy8RfE9K43xTdxNuU';

        await User.findByToken(token, 'auth', '', true, true);
      }
    } catch (err) {
      log(`Errore nell'iterazione ${i + 1}: ${err.message}`);
    }
    const diff = process.hrtime(start);
    const timeInSeconds = diff[0] + diff[1] / 1e9;
    timings.push(timeInSeconds);
    log(`Iterazione ${i + 1}: ${timeInSeconds.toFixed(3)} sec`);
  }

  const totalTime = timings.reduce((acc, t) => acc + t, 0);
  const averageTime = totalTime / timings.length;
  const minTime = Math.min(...timings);
  const maxTime = Math.max(...timings);

  log(`--- Risultati del test ${ind} ---`);
  log(`Tempo totale: ${totalTime.toFixed(3)} sec`);
  log(`Tempo medio: ${averageTime.toFixed(3)} sec`);
  log(`Tempo minimo: ${minTime.toFixed(3)} sec`);
  log(`Tempo massimo: ${maxTime.toFixed(3)} sec`);

  return { totalTime, averageTime, minTime, maxTime, timings, log: logString };
}

// Supponendo di usare Express e di avere già definito "router"
router.get('/testpao', async (req, res) => {
  try {
    let ind = req.query.ind;
    let numval = req.query.numval;
    const result = await testMongoPerformance(ind, numval);
    res.status(200).json({ log: result.log });
  } catch (error) {
    console.error('Errore nel test di performance:', error);
    res.status(500).json({ error: error.message });
  }
});

router.get('/loadsite/:userId/:idapp/:vers', authenticate_noerror_WithUserLean, async (req, res) => {
  try {
    let versionstr = req.params.vers;

    let version = tools.getVersionint(versionstr);

    return await load(req, res, version);
  } catch (error) {
    console.error('Errore durante il caricamento del sito:', error);
    return res.status(500).json({ error: 'Errore interno del server' });
  }
});

async function measurePromises(promises) {
  const keys = Object.keys(promises);
  const timings = {}; // memorizza il tempo per ogni promise
  const startTotal = process.hrtime(); // tempo iniziale totale

  // Avvolgo ogni promise per misurare il tempo
  const wrappedPromises = keys.map((key) => {
    const promise = promises[key];
    return (async () => {
      const start = process.hrtime(); // inizio timer per questa promise
      const result = await promise;
      const diff = process.hrtime(start);
      // Calcola i secondi (precisione in nanosecondi)
      const seconds = diff[0] + diff[1] / 1e9;
      timings[key] = seconds;
      return result;
    })();
  });

  // Attendo tutte le promise in parallelo
  const results = await Promise.all(wrappedPromises);
  const diffTotal = process.hrtime(startTotal);
  const totalTime = diffTotal[0] + diffTotal[1] / 1e9;

  // Ricostruisco l'oggetto data con i risultati
  const data = keys.reduce((acc, key, index) => {
    acc[key] = results[index];
    return acc;
  }, {});

  // Ordina le chiamate per tempo decrescente e prende le 10 più lente
  const slowCalls = Object.entries(timings)
    .sort(([, timeA], [, timeB]) => timeB - timeA)
    .slice(0, 10)
    .map(([key, time]) => ({ key, time }));

  return { data, totalTime, slowCalls };
}

async function load(req, res, version = '0') {
  try {
    // console.log(' ... 1) richiesta LOAD');

    // Estrazione e validazione degli input
    const userId = req.user ? req.user._id.toString() : req.params.userId || '0';
    const idapp = req.params.idapp;
    /*const status = req.code === server_constants.RIS_CODE_HTTP_TOKEN_EXPIRED
      ? server_constants.RIS_CODE_HTTP_TOKEN_EXPIRED
      : 200;*/

    let status = req.code;
    if (status === server_constants.RIS_CODE_OK) {
      status = 200;
    }

    const token = req.header('x-auth');
    // Se non ho il token, vado cmq stato a 200
    if (!token && status === server_constants.RIS_CODE_HTTP_INVALID_TOKEN) {
      status = 200;
    }

    // Determina se l'utente ha determinati permessi
    const gestoredelSito =
      req.user && (User.isAdmin(req.user.perm) || User.isManager(req.user.perm) || User.isEditor(req.user.perm))
        ? '1'
        : '0';

    const socioresidente = req.user && req.user.profile ? req.user.profile.socioresidente : false;

    // Costruzione dell'oggetto delle promesse
    const promises = {
      bookedevent:
        userId !== '0' ? Booking.findAllByUserIdAndIdApp(userId, idapp, gestoredelSito) : Promise.resolve([]),
      eventlist: MyEvent.findAllIdApp(socioresidente, idapp),
      operators: Operator.findAllIdApp(idapp),
      wheres: Where.findAllIdApp(idapp),
      contribtype: Contribtype.findAllIdApp(idapp),
      settings: Settings.findAllIdApp(idapp, false, false),
      permissions: Permission.findAllIdApp(),
      disciplines: Discipline.findAllIdApp(idapp),
      newstosent: gestoredelSito ? Newstosent.findAllIdApp(idapp) : Promise.resolve([]),
      mailinglist: Promise.resolve([]),
      mypage: version > 91 ? MyPage.findOnlyStruttRec(idapp) : MyPage.findAllIdApp(idapp),
      gallery: gestoredelSito ? Gallery.findAllIdApp(idapp) : Promise.resolve([]),
      paymenttype: PaymentType.findAllIdApp(idapp),
      calcstat: req.user ? User.calculateStat(idapp, req.user.username) : Promise.resolve(null),
      calzoom: CalZoom.findAllIdApp(idapp),
      producers: Producer.findAllIdApp(idapp),
      cart: req.user ? Cart.getCartByUserId(userId, idapp) : Promise.resolve(null),
      storehouses: Storehouse.findAllIdApp(idapp),
      departments: Department.findAllIdApp(idapp),
      orderscart: req.user
        ? User.isManager(req.user.perm)
          ? OrdersCart.getOrdersCartByUserId('ALL', idapp, 0, false)
          : OrdersCart.getOrdersCartByUserId(userId, idapp, 0, false)
        : Promise.resolve(null),
      groups: Group.findAllIdApp(idapp),
      resps: User.getusersRespList(idapp),
      workers: User.getusersWorkersList(idapp),
      internalpages: MyPage.findInternalPages(idapp),
      // Campi aggiuntivi per versioni >= 91
      levels: version >= 91 ? Level.findAllIdApp(idapp) : Promise.resolve([]),
      skills: version >= 91 ? Skill.findAllIdApp(idapp) : Promise.resolve([]),
      sectors: version >= 91 ? Sector.findAllIdApp(idapp) : Promise.resolve([]),
      statusSkills: version >= 91 ? StatusSkill.findAllIdApp(idapp) : Promise.resolve([]),
      provinces: version >= 91 ? Province.findAllIdApp(idapp) : Promise.resolve([]),
      catgrps: version >= 91 ? CatGrp.findAllIdApp(idapp) : Promise.resolve([]),
      adtypes: version >= 91 ? AdType.findAllIdApp(idapp) : Promise.resolve([]),
      adtypegoods: version >= 91 ? AdTypeGood.findAllIdApp(idapp) : Promise.resolve([]),
      sectorgoods: version >= 91 ? SectorGood.findAllIdApp(idapp) : Promise.resolve([]),
      sectorbachecas: version >= 91 ? SectorBacheca.findAllIdApp(idapp) : Promise.resolve([]),
      goods: version >= 91 ? Good.findAllIdApp(idapp) : Promise.resolve([]),
      bachecas: version >= 91 ? Bacheca.findAllIdApp(idapp) : Promise.resolve([]),
      site: version >= 91 ? Site.findAllIdApp(idapp) : Promise.resolve([]),
      mygroups: version >= 91 ? MyGroup.findAllGroups(idapp) : Promise.resolve([]),
      listcircuits: version >= 91 ? Circuit.findAllIdApp(idapp) : Promise.resolve([]),
      myelems: version >= 91 ? MyElem.findAllIdApp(idapp) : Promise.resolve([]),
      categories: version >= 91 ? Category.findAllIdApp(idapp) : Promise.resolve([]),
      providers: version >= 91 ? Provider.findAllIdApp(idapp) : Promise.resolve([]),
      scontisticas: version >= 91 ? Scontistica.findAllIdApp(idapp) : Promise.resolve([]),
      gasordines: version >= 91 ? Gasordine.findAllIdApp(idapp) : Promise.resolve([]),
      /*products: version >= 91
        ? Product.findAllIdApp(idapp, undefined, undefined, req.user ? User.isManager(req.user.perm) : false)
        : Promise.resolve([]),*/
      products: Promise.resolve([]),
      catprods: version >= 91 ? Product.getArrCatProds(idapp, shared_consts.PROD.BOTTEGA) : Promise.resolve([]),
      subcatprods: version >= 91 ? SubCatProd.findAllIdApp(idapp) : Promise.resolve([]),
      catprods_gas: version >= 91 ? Product.getArrCatProds(idapp, shared_consts.PROD.GAS) : Promise.resolve([]),
      catAI: version >= 91 ? CatAI.findAllIdApp(idapp) : Promise.resolve([]),
      authors: version >= 91 ? Author.findAllIdApp(idapp) : Promise.resolve([]),
      publishers: version >= 91 ? Publisher.getEditoriWithTitleCount(idapp) : Promise.resolve([]),
      myschedas: version >= 91 ? MyElem.findallSchedeTemplate(idapp) : Promise.resolve([]),
      collane: version >= 91 ? Collana.getCollaneWithTitleCount(idapp) : Promise.resolve([]),
      catalogs: version >= 91 ? Catalog.findAllIdApp(idapp) : Promise.resolve([]),
      catprtotali: version >= 91 ? CatProd.getCatProdWithTitleCount(idapp) : Promise.resolve([]),
      stati_prodotto: version >= 91 ? T_WEB_StatiProdotto.findAllIdApp() : Promise.resolve([]),
      tipologie: version >= 91 ? T_WEB_Tipologie.findAllIdApp() : Promise.resolve([]),
      tipoformato: version >= 91 ? T_WEB_TipiFormato.findAllIdApp() : Promise.resolve([]),
      crons: version >= 91 ? Cron.findAllIdApp() : Promise.resolve([]),
      raccoltacataloghis: version >= 91 ? RaccoltaCataloghi.findAllIdApp(idapp) : Promise.resolve([]),
      myuserextra: req.user ? User.addExtraInfo(idapp, req.user, version) : Promise.resolve(null),
      statuscode2: version >= 91 ? req.statuscode2 : Promise.resolve([]),
    };

    // Esecuzione parallela di tutte le promesse
    /*const keys = Object.keys(promises);
    const results = await Promise.all(Object.values(promises));
    const data = keys.reduce((acc, key, index) => {
      acc[key] = results[index];
      return acc;
    }, {});
    */

    const { data, totalTime, slowCalls } = await measurePromises(promises);
    // console.log('Risultati delle promise:', data);
    // console.log('Tempo di esecuzione:', totalTime, 'secondi');
    //console.log('Le 10 chiamate più lente:', slowCalls);

    // Aggiornamento delle informazioni dell'utente, se presente
    let myuser = req.user;
    if (myuser && data.myuserextra) {
      myuser = data.myuserextra;
      myuser.password = '';
      myuser.calcstat = data.calcstat;
    }

    // Costruzione dell'oggetto di risposta in base alla versione
    let responseData;
    if (version < 91) {
      responseData = {
        bookedevent: data.bookedevent,
        eventlist: data.eventlist,
        operators: data.operators,
        wheres: data.wheres,
        contribtype: data.contribtype,
        settings: data.settings,
        permissions: data.permissions,
        disciplines: data.disciplines,
        newstosent: data.newstosent,
        mailinglist: data.mailinglist,
        mypage: data.mypage,
        gallery: data.gallery,
        paymenttypes: data.paymenttype,
        calzoom: data.calzoom,
        producers: data.producers,
        cart: data.cart,
        storehouses: data.storehouses,
        departments: data.departments,
        orders: data.orderscart,
        groups: data.groups,
        resps: data.resps,
        workers: data.workers,
        myuser,
        internalpages: data.internalpages,
      };
    } else {
      responseData = {
        bookedevent: data.bookedevent,
        eventlist: data.eventlist,
        operators: data.operators,
        wheres: data.wheres,
        contribtype: data.contribtype,
        settings: data.settings,
        permissions: data.permissions,
        disciplines: data.disciplines,
        newstosent: data.newstosent,
        mailinglist: data.mailinglist,
        mypage: data.mypage,
        gallery: data.gallery,
        paymenttypes: data.paymenttype,
        calzoom: data.calzoom,
        producers: data.producers,
        cart: data.cart,
        storehouses: data.storehouses,
        departments: data.departments,
        orders: data.orderscart,
        groups: data.groups,
        resps: data.resps,
        workers: data.workers,
        myuser,
        internalpages: data.internalpages,
        levels: data.levels,
        skills: data.skills,
        sectors: data.sectors,
        statusSkills: data.statusSkills,
        provinces: data.provinces,
        catgrps: data.catgrps,
        adtypes: data.adtypes,
        adtypegoods: data.adtypegoods,
        sectorgoods: data.sectorgoods,
        goods: data.goods,
        site: data.site,
        mygroups: data.mygroups,
        listcircuits: data.listcircuits,
        myelems: data.myelems,
        categories: data.categories,
        providers: data.providers,
        scontisticas: data.scontisticas,
        gasordines: data.gasordines,
        products: data.products,
        catprods: data.catprods,
        subcatprods: data.subcatprods,
        catprods_gas: data.catprods_gas,
        catAI: data.catAI,
        code: req.code,
        authors: data.authors,
        publishers: data.publishers,
        myschedas: data.myschedas,
        collane: data.collane,
        catalogs: data.catalogs,
        catprtotali: data.catprtotali,
        stati_prodotto: data.stati_prodotto,
        tipologie: data.tipologie,
        tipoformato: data.tipoformato,
        crons: data.crons,
        raccoltacataloghis: data.raccoltacataloghis,
        statuscode2: data.statuscode2,
        sectorbachecas: data.sectorbachecas,
        bachecas: data.bachecas,
      };
    }

    // console.log(' ... 2) load dati caricati ...');
    return res.status(status).send(responseData);
  } catch (e) {
    console.error('Errore in load:', e);
    return res.status(400).send({ error: e.message });
  }
}

router.get('/checkupdates', authenticate_noerror, async (req, res) => {
  try {
    const idapp = req.query.idapp;

    if (!req.user) {
      if (req.code === 1) return res.status(200).send();
      else return res.status(req.code).send();
    }

    await CfgServer.find({ idapp })
      .then(async (arrcfgrec) => {
        if (arrcfgrec.length === 0) {
          if (User.isAdmin(req.user.perm)) {
            // crea un nuovo record
            const mycfgServer = new CfgServer();
            mycfgServer.idapp = idapp;
            mycfgServer.chiave = 'vers';
            mycfgServer.userId = 'ALL';
            mycfgServer.valore = await tools.getVersServer();

            mycfgServer.save();

            arrcfgrec = await CfgServer.find({ idapp });
          } else {
            return res.status(404).send();
          }
        }

        // ++Add to Log Stat ....

        let last_msgs = null;
        let last_notifs = null;
        let last_notifcoins = null;
        let usersList = null;
        let last_notifcoins_inattesa = null;
        // const sall = '0';

        // msgs = SendMsg.findAllByUserIdAndIdApp(userId, req.user.username, req.user.idapp);
        if (req.user) {
          const userId = req.user._id;
          if (!ObjectId.isValid(userId)) {
            return res.status(404).send();
          }

          last_msgs = SendMsg.findLastGroupByUserIdAndIdApp(userId, req.user.username, idapp);
          last_notifs = SendNotif.findLastNotifsByUserIdAndIdApp(req.user.username, idapp, 40);
          // Se sono il Gestore, le ricevo tutte quante:
          if (User.isAdmin(req.user.perm)) {
            last_notifcoins_inattesa = SendNotif.findAllNotifCoinsAllIdAndIdApp(idapp);
          } else {
            last_notifcoins_inattesa = SendNotif.findLastNotifCoinsByUserIdAndIdApp(
              req.user.username,
              idapp,
              200,
              true
            );
          }
          last_notifcoins = SendNotif.findLastNotifCoinsByUserIdAndIdApp(req.user.username, idapp, 1, false);

          if (req.user) {
            // If User is Admin, then send user Lists
            if (User.isAdmin(req.user.perm) || User.isEditor(req.user.perm) || User.isManager(req.user.perm)) {
              // Send UsersList
              usersList = User.getUsersList(idapp);
              // usersList = null;
            }
          }
        }

        return Promise.all([usersList, last_msgs, last_notifs, last_notifcoins, last_notifcoins_inattesa]).then(
          (arrdata) => {
            // console.table(arrdata);
            return res.send({
              CfgServer: arrcfgrec,
              usersList: arrdata[0],
              last_msgs: arrdata[1],
              last_notifs: arrdata[2],
              last_notifcoins: [...arrdata[4], ...arrdata[3]],
            });
          }
        );
      })
      .catch((e) => {
        console.log(e.message);
        res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: e });
      });
  } catch (e) {
    console.error('error:', e);
  }
});

router.post('/upload_from_other_server/:dir', authenticate, (req, res) => {
  // const dir = req.params.dir;
  // const idapp = req.user.idapp;
  /*
    const form = new formidable.IncomingForm();
 
    form.parse(req);
 
    const client = new ftp(process.env.FTPSERVER_HOST, process.env.FTPSERVER_PORT, process.env.FTPSERVER_USER + idapp + '@associazioneshen.it', process.env.FTPSERVER_PWD + idapp, false, 134217728);
 
    // SSL_OP_NO_TLSv1_2 = 134217728
 
    // console.log('client', client);
 
    form.uploadDir = folder + '/' + dir;
    try {
 
      form.on('fileBegin', async function (name, file){
        file.path = folder + '/' + file.name;
      });
 
      form.on('file', async function (name, file){
        try {
          // Create directory remote
 
          if (!!dir)
            await client.createDir(dir);
 
          const miofile = (dir) ? dir + ` / ` + file.name : file.name;
          console.log('Upload...');
          const ret = await client.upload(file.path, miofile, 755);
          console.log('Uploaded ' + file.name, 'status:', ret);
          if (!ret)
            res.status(400).send();
          else {
            // Delete file from local directory
            fs.unlinkSync(file.path);
            res.end();
          }
        }catch (e) {
          console.log('error', e);
          res.status(400).send();
        }
      });
 
      form.on('aborted', () => {
        console.error('Request aborted by the user');
        res.status(400).send();
      });
 
      form.on('error', (err) => {
        console.error('Error Uploading', err);
        res.status(400).send();
      });
 
    } catch (e) {
      console.log('Error', e)
    }
  */
});

// Funzione principale che gestisce l'upload
async function uploadFile(req, res, version, options = {}) {
  try {
    const quality = options.quality || 'original';
    const dirParam = req.params.dir || '';
    const dir = tools.invertescapeslash(dirParam);
    const idapp = req.user?.idapp;

    if (!idapp) {
      return res.status(400).send('ID applicazione mancante');
    }

    // Determina la cartella base
    const dirmain = version > 0 && tools.sulServer() ? '' : server_constants.DIR_PUBLIC_LOCALE;
    const baseUploadFolder = tools.getdirByIdApp(idapp) + dirmain + server_constants.DIR_UPLOAD;

    // Directory di upload specifica
    const uploadDir = path.join(baseUploadFolder, dir);

    // Crea la cartella se non esiste
    await tools.mkdirpath(uploadDir);

    // Configura formidable
    const form = new formidable.IncomingForm({ uploadDir });

    // Parsing in modalità Promise
    const files = await new Promise((resolve, reject) => {
      form.parse(req, (err, fields, files) => {
        if (err) return reject(err);
        resolve(files);
      });
    });

    // Gestione file
    for (const key in files) {
      if (!Object.prototype.hasOwnProperty.call(files, key)) continue;

      const file = files[key][0];
      console.log('File ricevuto:', file.originalFilename);

      const oldFile = file.filepath || file.path;
      //const newFilePath = path.join(uploadDir, `${file.newFilename}_${file.originalFilename}`);
      const newFilePath = path.join(uploadDir, `${file.originalFilename}`);

      //@@ATTENZIONE ! HO RIMESSO COM'ERA PRIMA ! MA NON MI CONVINCE !
      // RICONTROLLARE SE DEVO METTERLGI UN SUFFISSO PRIMA... (newFilePath)

      // Sposta e rinomina
      await tools.move(oldFile, newFilePath);

      // Ridimensionamento opzionale
      await handleImageResizing(newFilePath, quality);

      console.log(`File processato e salvato in: ${newFilePath}`);
    }

    return res.status(200).send('Upload completato con successo');
  } catch (err) {
    console.error('Errore durante uploadFile:', err);
    if (!res.headersSent) {
      return res.status(500).send("Errore durante l'upload");
    }
  }
}

// Funzione per muovere il file nella destinazione finale
async function moveFile(fromfile, tofile, res) {
  console.log('Moving file from ' + fromfile + ' to ' + tofile);
  try {
    if (false && !tools.sulServer()) {
      console.log('Copying file (locally):', fromfile, 'to', tofile);
      await tools.execScriptNoOutput("sudo cp -R '" + fromfile + "' '" + tofile + "'");
      res.end();
      return;
    }
    await tools.move(fromfile, tofile);
  } catch (error) {
    console.log('Error moving file:', error);
    res.status(400).send();
  }
}

// Funzione per gestire il ridimensionamento dell'immagine
async function handleImageResizing(filePath, quality) {
  const resizedImgPath =
    tools.extractFilePath(filePath) + '/' + server_constants.PREFIX_IMG + tools.extractFileName(filePath);
  const resizedSmallPath =
    tools.extractFilePath(filePath) + '/' + server_constants.PREFIX_IMG_SMALL + tools.extractFileName(filePath);

  try {
    // Ridimensionamento per la qualità "small"

    if (quality === 'small' || quality === 'both') {
      await resizeImage(filePath, resizedSmallPath, 64, 64);
    }

    // Ridimensionamento per la qualità "medium"
    if (quality === 'medium' || quality === 'both') {
      await resizeImage(filePath, resizedImgPath, 512, 512);
    }

    // Se la qualità è "original", non fare il ridimensionamento
    if (quality === 'original') {
      console.log('Keeping original size for image: ' + filePath);
      return;
    }
  } catch (err) {
    console.error('Error resizing image: ', err);
  }
}

// Funzione per ridimensionare l'immagine
async function resizeImage(inputPath, outputPath, width, height) {
  try {
    await sharp(inputPath).resize(width, height, { fit: sharp.fit.contain }).withMetadata().toFile(outputPath);
    console.log('Image resized to ' + width + 'x' + height + ' and saved as ' + outputPath);
  } catch (err) {
    console.error('Error resizing image:', err);
  }
}
router.post('/upload/:dir', authenticate, (req, res) => {
  return uploadFile(req, res, 0, req.body.options);
});

router.post('/uploadnew/:vers/:dir/', authenticate_noerror_WithUser, (req, res) => {
  let versionstr = req.params.vers;
  let version = tools.getVersionint(versionstr);

  try {
    return uploadFile(req, res, version, req.body.options);
  } catch (e) {
    console.log('error', e);
    res.status(400).send();
  }
});

router.delete('/delfile/:vers', authenticate, async (req, res) => {
  let versionstr = req.params.vers;
  let version = tools.getVersionint(versionstr);
  await deleteFile(req, res, version);
});

router.delete('/delfile', authenticate, async (req, res) => {
  await deleteFile(req, res, 0);
});

async function deleteFile(req, res, version) {
  const relativefile = req.query.filename;
  const idapp = req.user.idapp;

  if (!relativefile || relativefile.endsWith('/')) {
    return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
  }

  try {
    let dirmain = '';
    if (version > 0) {
      if (!tools.sulServer()) {
        dirmain = server_constants.DIR_PUBLIC_LOCALE;
      }
    }

    try {
      console.log('Delete file ' + relativefile);
      // ++ Move in the folder application !
      let fullpathfile = tools.getdirByIdApp(idapp) + dirmain + '/' + relativefile.replace(/^\//, '');

      await tools.delete(fullpathfile, true, (err) => {
        if (err) console.log('err', err);
        if (err === undefined || err.errno === -2) return res.send({ code: server_constants.RIS_CODE_OK, msg: '' });
      });
    } catch (e) {
      console.log('error', e?.message);
      res.status(400).send();
    }
  } catch (e) {
    console.log('Error', e?.message);
  }
}

module.exports = router;
