const bcrypt = require('bcryptjs');
const mongoose = require('mongoose').set('debug', false);
const validator = require('validator');
const jwt = require('jsonwebtoken');
const _ = require('lodash');


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

const {Settings} = require('../models/settings');
// const {ListaIngresso} = require('../models/listaingresso');
const {Graduatoria} = require('../models/graduatoria');
// const {Nave} = require('../models/nave');
// const {NavePersistente} = require('../models/navepersistente');
// const { ExtraList } = require('../models/extralist');

const {MyGroup} = require('../models/mygroup');

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

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

mongoose.Promise = global.Promise;

mongoose.level = 'F';
// Resolving error Unknown modifier: $pushAll
mongoose.plugin(schema => {
  schema.options.usePushEach = true;
});

mongoose.set('debug', process.env.DEBUG);

const UserSchema = new mongoose.Schema({
      userId: {
        type: String,
      },
      email: {
        type: String,
        required: true,
        trim: true,
        minlength: 1,
        unique: false,
        /*validate: {
          validator: validator.isEmail,
          message: '{VALUE} is not a valid email'
        }*/
      },
      idapp: {
        type: String,
        required: true,
      },
      group: {
        type: Number,
      },
      index: {
        type: Number,
      },
      ind_order: {
        type: Number,
      },
      old_order: {
        type: Number,
      },
      username: {
        type: String,
        required: true,
        trim: true,
        minlength: 6,
        unique: false,
      },
      name: {
        type: String,
        trim: true,
      },
      surname: {
        type: String,
        trim: true,
      },
      password: {
        type: String,
        require: true,
        minlength: 6,
      },
      lang: {
        type: String,
        require: true,
      },
      linkreg: {
        type: String,
        required: false,
      },
      verified_email: {
        type: Boolean,
      },
      made_gift: {
        type: Boolean,
      },
      tokens: [
        {
          access: {
            type: String,
            required: true,
          },
          browser: {
            type: String,
            required: true,
          },
          token: {
            type: String,
            required: true,
          },
          date_login: {
            type: Date,
          },
        }],
      perm: {
        type: Number,
      },
      ipaddr: {
        type: String,
      },
      date_reg: {
        type: Date,
      },
      date_deleted: {
        type: Date,
      },
      date_tokenforgot: {
        type: Date,
      },
      tokenforgot: {
        type: String,
      },
      lasttimeonline: {
        type: Date,
      },
      useragent: {
        type: String,
      },
      news_on: {
        type: Boolean,
      },
      aportador_solidario: {  // da cancellare
        type: String,
      },
      verified_by_aportador: {
        type: Boolean,
      },
      notask_verif: {
        type: Boolean,
      },
      trust_modified: {
        type: Date,
      },
      aportador_iniziale: {
        type: String,
      },
      aportador_solidario_nome_completo: {
        type: String,
      },
      aportador_solidario_ind_order: {
        type: Number,
      },
      note: {
        type: String,
      },
      deleted: {
        type: Boolean,
        default: false,
      },
      sospeso: {
        type: Boolean,
      },
      blocked: {
        type: Boolean,
      },
      username_who_block: {
        type: String,
      },
      date_blocked: {
        type: Date,
      },
      non_voglio_imbarcarmi: {
        type: Boolean,
      },
      navinonpresenti: {
        type: Boolean,
      },
      subaccount: {
        type: Boolean,
      },
      cart: {
        type: Object,
      },
      profile: {
        img: {
          type: String,
        },
        nationality: {
          type: String,
        },
        intcode_cell: {
          type: String,
        },
        iso2_cell: {
          type: String,
        },
        cell: {
          type: String,
        },
        country_pay: {
          type: String,
        },
        email_paypal: {
          type: String,
        },
        payeer_id: {
          type: String,
        },
        advcash_id: {
          type: String,
        },
        revolut: {
          type: String,
        },
        link_payment: {
          type: String,
        },
        note_payment: {
          type: String,
        },
        paymenttypes: [],
        username_telegram: {
          type: String,
        },
        firstname_telegram: {
          type: String,
        },
        lastname_telegram: {
          type: String,
        },
        website: {
          type: String,
        },
        teleg_id: {
          type: Number,
        },
        teleg_id_old: {
          type: Number,
        },
        teleg_checkcode: {
          type: Number,
        },
        manage_telegram: {
          type: Boolean,
        },
        resplist: {
          type: Boolean,
        },
        workerslist: {
          type: Boolean,
        },
        dateofbirth: {
          type: Date,
        },
        born_city: {
          type: String,
          trim: true,
        },
        born_city_id: {
          type: Number,
        },
        born_province: {
          type: String,
          trim: true,
        },
        born_country: {
          type: String,
          trim: true,
        },
        my_dream: {
          type: String,
        },
        saw_and_accepted: {
          type: Number,
        },
        saw_zoom_presentation: {
          type: Boolean,
        },
        ask_zoom_partecipato: {
          type: Boolean,
        },
        qualified: {
          type: Boolean,
        },
        qualified_2invitati: {
          type: Boolean,
        },
        special_req: {
          type: Boolean,
        },
        sex: {
          type: Number,
        },
        biografia: {
          type: String,
        },
        qualifica: {
          type: String,
        },
        motivazioni: {
          type: String,
        },
        competenze_professionalita: {
          type: String,
        },
        cosa_offrire: {
          type: String,
        },
        cosa_ricevere: {
          type: String,
        },
        altre_comunicazioni: {
          type: Boolean,
        },
        come_ci_hai_conosciuto: {
          type: Boolean,
        },
        socio: {
          type: Boolean,
        },
        socioresidente: {
          type: Boolean,
        },
        consiglio: {
          type: Boolean,
        },
        myshares: [
          {
            description: {type: String},
            rating: {type: Number},
          }],
        friends: [
          {
            _id: false,
            username: {type: String},
            date: {type: Date},
          }],  // username
        req_friends: [
          {
            _id: false,
            username: {type: String},
            date: {type: Date},
          }],  // username
        mygroups: [
          {
            _id: false,
            groupname: {type: String},
            date: {type: Date},
          }],  // username
      },
    })
;

UserSchema.methods.toJSON = function() {
  const user = this;
  const userObject = user.toObject();

  return _.pick(userObject, ['_id', ...shared_consts.fieldsUserToChange()]);
};

UserSchema.methods.generateAuthToken = function(req) {
  // console.log("GENERA TOKEN : ");
  const user = this;

  const useragent = req.get('User-Agent');
  // tools.mylog("GENERATE USER-AGENT = ", useragent);

  const access = 'auth';
  const browser = useragent;
  const token = jwt.sign({_id: user._id.toHexString(), access},
      process.env.SIGNCODE).toString();
  const date_login = new Date();

  // CANCELLA IL PRECEDENTE !
  user.tokens = user.tokens.filter(function(tok) {
    return (tok.access !== access) ||
        ((tok.access === access) && (tok.browser !== browser));
  });
  user.tokens.push({access, browser, token, date_login});

  user.lasttimeonline = new Date();

  return user.save().then(() => {
    // console.log("TOKEN CREATO IN LOGIN : " + token);
    return token;
  }).catch(err => {
    console.log('Error', err.message);
  });
};

UserSchema.statics.setPermissionsById = function(id, perm) {
  const user = this;

  return user.findByIdAndUpdate(id, {$set: {perm}}).then((user) => {
    if (user)
      return res.send({code: server_constants.RIS_CODE_OK, msg: ''});
    else
      return res.send({code: server_constants.RIS_CODE_ERR, msg: ''});
  });

};

UserSchema.statics.setZoomPresenza = async function(idapp, id, presenza) {
  const User = this;

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

  let allData = {};
  allData.myuser = await User.getUserById(idapp, id);
  if (!!allData.myuser)
    allData.precDataUser = await User.getInfoUser(idapp,
        allData.myuser.username);

  return User.findByIdAndUpdate(id,
      {$set: {'profile.saw_zoom_presentation': presenza}}).then((rec) => {
    if (presenza) {
      const messaggio = tools.get__('ZOOM_CONFERMATO');
      telegrambot.sendMsgTelegram(rec.idapp, rec.username, messaggio);
      telegrambot.sendMsgTelegramToTheManagersAndZoomeri(idapp,
          `L\'utente ${rec.name} ${rec.surname} (${rec.username}) è stato confermato per aver visto lo Zoom di Benvenuto`);
    } else {
      telegrambot.sendMsgTelegramToTheManagersAndZoomeri(idapp,
          `L\'utente ${rec.name} ${rec.surname} (${rec.username}) è stato annullata la sua richiesta per aver visto lo Zoom di Benvenuto! (Non ci risulta)`);
    }

    return User.findByIdAndUpdate(id,
        {$set: {'profile.ask_zoom_partecipato': false}}).then((user) => {

      User.checkIfSbloccatiRequisiti(idapp, allData, id);
    });
  });

};

UserSchema.statics.canHavePower = function(perm) {
  const User = this;

  try {
    let consentito = false;
    if (User.isAdmin(perm) || User.isManager(perm) ||
        User.isEditor(perm) || User.isTutor(perm)) {
      consentito = true;
    }

    return consentito;
  } catch (e) {
    return false;
  }
};

UserSchema.statics.isAdmin = function(perm) {
  try {
    return ((perm & shared_consts.Permissions.Admin) ===
        shared_consts.Permissions.Admin);
  } catch (e) {
    return false;
  }
};

UserSchema.statics.isManager = function(perm) {
  try {
    return ((perm & shared_consts.Permissions.Manager) ===
        shared_consts.Permissions.Manager);
  } catch (e) {
    return false;
  }
};

UserSchema.statics.isEditor = function(perm) {
  try {
    return ((perm & shared_consts.Permissions.Editor) ===
        shared_consts.Permissions.Editor);
  } catch (e) {
    return false;
  }
};

UserSchema.statics.isZoomeri = function(perm) {
  try {
    return ((perm & shared_consts.Permissions.Zoomeri) ===
        shared_consts.Permissions.Zoomeri);
  } catch (e) {
    return false;
  }
};

UserSchema.statics.isDepartment = function(perm) {
  try {
    return ((perm & shared_consts.Permissions.Zoomeri) ===
        shared_consts.Permissions.Department);
  } catch (e) {
    return false;
  }
};

UserSchema.statics.isTutor = function(perm) {
  try {
    return ((perm & shared_consts.Permissions.Tutor) ===
        shared_consts.Permissions.Tutor);
  } catch (e) {
    return false;
  }
};

UserSchema.statics.findByToken = function(token, typeaccess) {
  const User = this;
  let decoded;

  try {
    decoded = jwt.verify(token, process.env.SIGNCODE);
  } catch (e) {
    return Promise.resolve(null);
  }

  return User.findOne({
    '_id': decoded._id,
    'tokens.token': token,
    'tokens.access': typeaccess,
  });
};

UserSchema.statics.findByTokenAnyAccess = function(token) {
  const User = this;
  let decoded;

  try {
    decoded = jwt.verify(token, process.env.SIGNCODE);
  } catch (e) {
    return Promise.resolve(null);
  }

  return User.findOne({
    '_id': decoded._id,
    'tokens.token': token,
  });
};

UserSchema.statics.findByCredentials = function(idapp, username, password, pwdcrypted) {
  const User = this;
  let pwd = '';

  return User.findOne({
    idapp, username: username,
    $or: [
      {deleted: {$exists: false}},
      {deleted: {$exists: true, $eq: false}},
      {
        $and: [
          {deleted: {$exists: true, $eq: true}},
          {subaccount: {$exists: true, $eq: true}},
        ],
      },
    ],

  }).then((user) => {
    if (!user) {
      // Check if with email:
      return User.findOne({
        idapp, email: username.toLowerCase(),
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      }).lean();
    } else {
      return !user.deleted || (user.deleted && user.subaccount) ? user : null;
    }
  }).then(user => {
    if (!user)
      return null;

    pwd = user.password;

    if (pwdcrypted) {
      if (pwd === user.password){
        return user
      } else {
        return false
      }
    }

    return new Promise((resolve, reject) => {
      // Use bcrypt.compare to compare password and user.password
      // console.log("pwd1 " + password);
      // console.log("pwd2 " + pwd);
      bcrypt.compare(password, pwd, (err, res) => {
        if (res) {
          resolve(user);
        } else {
          return resolve(null);
        }
      });
    });
  });
};

UserSchema.statics.findByUsername = async function(idapp, username, alsoemail, onlyifVerifiedByAportador) {
  const User = this;

  const myreg = ['^', username, '$'].join('');
  let regexusername = new RegExp(myreg, 'i');

  //++TODO: Set only the necessary fields to get in memory

  return User.findOne({
    idapp: idapp,
    username: {$regex: regexusername},
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).then((ris) => {
    if ((!ris) && (alsoemail)) {
      regexemail = new RegExp(['^', username.toLowerCase(), '$'].join(''), 'i');

      return User.findOne({
        'idapp': idapp,
        'email': {$regex: regexemail},
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      });
    }
    return ris;
  }).then((rec) => {
    if (onlyifVerifiedByAportador) {
      if (tools.getAskToVerifyReg(idapp)) {
        if (!rec.verified_by_aportador)
          return null;

      }
    }

    return rec;
  });
};

UserSchema.statics.getUserShortDataByUsername = async function(
    idapp, username) {
  const User = this;

  const myrec = await User.findOne({
    'idapp': idapp,
    'username': username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {
    idapp: 1,
    lang: 1,
    index: 1,
    ind_order: 1,
    username: 1,
    aportador_solidario: 1,
    name: 1,
    surname: 1,
    deleted: 1,
    sospeso: 1,
    verified_email: 1,
    verified_by_aportador: 1,
    'profile.teleg_id': 1,
    // 'profile.saw_zoom_presentation': 1,
    'profile.ask_zoom_partecipato': 1,
    'profile.qualified': 1,
    'profile.qualified_2invitati': 1,
    'profile.saw_and_accepted': 1,
    'profile.email_paypal': 1,
    'profile.payeer_id': 1,
    'profile.advcash_id': 1,
    'profile.revolut': 1,
    'profile.link_payment': 1,
    'profile.note_payment': 1,
    // 'profile.my_dream': 1,
    'profile.paymenttypes': 1,
    'profile.cell': 1,
    made_gift: 1,
    email: 1,
    date_reg: 1,
    img: 1,
  }).then((ris) => {
    if (!!ris) {
      // console.log('ris', ris);
      if (!!ris._doc)
        return ris._doc;
      else
        return null;
    }
  });

  if (myrec) {
    myrec.qualified = await User.isUserQualified7(idapp, myrec.username);
    myrec.numNaviEntrato = 0;
    // myrec.numinvitati = await ListaIngresso.getnumInvitati(idapp, myrec.username);
    // myrec.numinvitatiattivi = await ListaIngresso.getnumInvitatiAttivi(idapp, myrec.username);
  }

  return myrec;
};

UserSchema.statics.getDownlineByUsername = async function(
    idapp, username, includemyself, onlynumber) {

  if (username === undefined)
    return null;

  let arrrec = [];

  let myq = {
    idapp,
    aportador_solidario: username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  };

  if (!includemyself) {
    myq = {...myq, username: {$ne: username}};
  }

  return arrrec;

};

UserSchema.statics.getQueryQualified = function() {
  return [
    {
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    },
    {
      $or: [
        {
          'profile.special_req': true,
        },
        {
          verified_email: true,
          'profile.teleg_id': {$gt: 1},
          'profile.saw_and_accepted': shared_consts.ALL_SAW_AND_ACCEPTED,
          // 'profile.saw_zoom_presentation': true,
          $or: [
            {'profile.link_payment': {$exists: true}},
            {'profile.email_paypal': {$exists: true}},
            {'profile.payeer_id': {$exists: true}},
            {'profile.advcash_id': {$exists: true}},
            {'profile.revolut': {$exists: true}},
          ],
          // 'profile.paymenttypes': { "$in": ['paypal'] },
          // $where: "this.profile.paymenttypes.length >= 1",
        }],
    },
  ];
};

UserSchema.statics.isUserQualified7 = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
    $and: User.getQueryQualified(),
  };

  const myrec = await User.findOne(myquery);

  return !!myrec;
};

UserSchema.statics.isUserResidente = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOne(myquery);
  if (!!myrec) {
    return myrec.profile.socioresidente;
  } else {
    return false;
  }

};

UserSchema.statics.isUserConsiglio = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOne(myquery);
  if (!!myrec) {
    return myrec.profile.consiglio;
  } else {
    return false;
  }

};

UserSchema.statics.isUserVisuProjects = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOne(myquery);
  if (!!myrec) {
    return myrec.profile.socioresidente;
  } else {
    return false;
  }

};

UserSchema.statics.isUserAlreadyQualified = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
    'profile.qualified': {$exists: true, $eq: true},
  };

  const myrec = await User.findOne(myquery);

  return !!myrec;
};

UserSchema.statics.isUserAlreadyQualified_2Invitati = async function(
    idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
    'profile.qualified_2invitati': {$exists: true, $eq: true},
  };

  const myrec = await User.findOne(myquery);

  return !!myrec;
};

UserSchema.statics.setUserQualified = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOneAndUpdate(myquery,
      {$set: {'profile.qualified': true}}, {new: false});

  return !!myrec;
};

UserSchema.statics.setVerifiedByAportador = async function(
    idapp, username, valuebool) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const userver = await User.findOne(myquery, {verified_by_aportador: 1}).lean();

  if (userver.verified_by_aportador !== valuebool) {
    const myrec = await User.findOneAndUpdate(myquery,
        {$set: {'verified_by_aportador': valuebool}}, {new: false});

    return !!myrec;
  } else {
    return false;
  }


};

UserSchema.statics.setnotask_verif = async function(
    idapp, username, valuebool) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOneAndUpdate(myquery,
      {$set: {'notask_verif': valuebool}}, {new: false});

  return !!myrec;
};

UserSchema.statics.setaportador_solidario = async function(
    idapp, username, usernameAportador) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOneAndUpdate(myquery,
      {$set: {'aportador_solidario': usernameAportador}}, {new: false});

  return !!myrec;
};

UserSchema.statics.setVerifiedByAportadorToALL = async function() {

  return User.updateMany({}, {$set: {'verified_by_aportador': true}},
      {new: false});

};

UserSchema.statics.setUserQualified_2Invitati = async function(
    idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  const myquery = {
    'idapp': idapp,
    'username': username,
  };

  const myrec = await User.findOneAndUpdate(myquery,
      {$set: {'profile.qualified_2invitati': true}}, {new: false});

  return !!myrec;
};

/*
UserSchema.statics.isUserQualified9 = async function(idapp, username) {
  const User = this;

  if (username === undefined)
    return false;

  qualified = await User.isUserQualified7(idapp, username);
  // numinvitatiattivi = await ListaIngresso.getnumInvitatiAttivi(idapp, username);

  return qualified && (numinvitatiattivi >= 2);

};
 */

/*
UserSchema.statics.getnumPaymentOk = function (idapp) {
  const User = this;

  return User.count({
    idapp,
    $and: [
      {
        $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
        'profile.paymenttypes': { "$in": ['paypal'] },
      },
      {
        $or: [
          { 'profile.email_paypal': { $exists: true } },
          { 'profile.revolut': { $exists: true } },
        ]
      }
    ]
  });
};
*/

UserSchema.statics.getUsersNationalityQuery = function(idapp) {
  const query = [
    {
      $match: {
        idapp,
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      },
    },
    {
      $group: {_id: '$profile.nationality', count: {$sum: 1}},
    },
    {
      $sort: {count: -1},
    },
  ];
  return query;
};

UserSchema.statics.getindOrderDuplicate = function(idapp) {
  const User = this;

  return User.aggregate(User.getUsersNationalityQuery(idapp)).then(ris => {
    // console.table(ris);
    return ris;
  });
};

UserSchema.statics.findByLinkreg = function(idapp, linkreg) {
  const User = this;

  return User.findOne({
    'linkreg': linkreg,
    'idapp': idapp,
  });
};

UserSchema.statics.AportadorOrig = function(idapp, id) {
  const User = this;

  return User.findOne({
    '_id': id,
    'idapp': idapp,
  }).then((rec) => {
    if (rec)
      return rec.aportador_iniziale;
    else
      return '';
  });
};

UserSchema.statics.findByLinkTokenforgot = function(idapp, email, tokenforgot) {
  const User = this;

  return User.findOne({
    'email': email,
    'tokenforgot': tokenforgot,
    'date_tokenforgot': {$gte: tools.IncDateNow(-1000 * 60 * 60 * 4)},  // 4 ore fa!
    'idapp': idapp,
  });
};

UserSchema.statics.findByEmail = function(idapp, email, onlyifVerifiedByAportador) {
  const User = this;

  return User.findOne({
    'idapp': idapp,
    'email': email,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).then((rec) => {
    if (onlyifVerifiedByAportador) {
      if (tools.getAskToVerifyReg(idapp)) {
        if (!rec.verified_by_aportador)
          return null;

      }
    }

    return rec;
  });
};

UserSchema.statics.getLastUser = function(idapp) {
  const User = this;

  return User.findOne({
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).sort({index: -1});
};

UserSchema.statics.findByIndOrder = function(idapp, ind_order) {
  const User = this;

  return User.getRecByIndOrder(idapp, ind_order);

};

UserSchema.statics.findByIndex = function(idapp, index) {
  const User = this;

  try {
    // ++Todo: non mettere tutti i campi !!
    return User.findOne({
      idapp,
      index,
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    });
  } catch (e) {
    console.error(e.message);
  }
};

UserSchema.statics.findByOldOrder = function(idapp, old_order) {
  const User = this;

  try {
    return User.findOne({
      idapp,
      old_order,
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    });
  } catch (e) {
    console.error(e.message);
  }
};

UserSchema.pre('save', async function(next) {

  try {
    if (this.isNew) {
      try {
        const myrec = await User.findOne({idapp: this.idapp}).
            limit(1).
            sort({index: -1});

        if (!!myrec) {
          this.index = myrec._doc.index + 1;
        } else {
          this.index = 1;
        }
      } catch (e) {
        this.index = 2;
      }
    }

    /*
    if (user.isModified('password')) {
      bcrypt.genSalt(10, (err, salt) => {
        bcrypt.hash(user.password, salt, (err, hash) => {
          user.password = hash;
          next();
        });
      });
    } else {
      next();
    }
    */

    next();
  } catch (e) {
    console.error(e.message);
  }
});

UserSchema.methods.removeToken = function(token) {
  const user = this;

  return user.updateOne({
    $pull: {
      tokens: {token},
    },
  });
};

UserSchema.statics.getEmailByUsername = async function(idapp, username) {
  const User = this;

  return User.findOne({
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).then((arrrec) => {
    return ((arrrec) ? arrrec.email : '');
  }).catch((e) => {
    console.error('getEmailByUsername', e);
  });
};

UserSchema.statics.getUsernameById = async function(idapp, id) {
  const User = this;

  return User.findOne({
    idapp, _id: id,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {username: 1}).then((myuser) => {
    return ((myuser) ? myuser.username : '');
  }).catch((e) => {

  });
};

UserSchema.statics.getUserById = function(idapp, id) {
  const User = this;

  return User.findOne({
    idapp, _id: id,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  });
};

UserSchema.statics.getUserByUsername = function(idapp, username) {
  const User = this;

  return User.findOne({
    idapp,
    username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  });
};

UserSchema.statics.isMyFriend = async function(idapp, username, myusername) {

  const myfriends = await User.getUsernameFriendsByUsername(idapp, myusername);
  if (myfriends) {
    return myfriends.includes(username);
  } else {
    return false;
  }

};

UserSchema.statics.getUserProfileByUsername = async function(
    idapp, username, myusername, usaSuperPower, myperm = '') {
  const User = this;
  // If is my Friend, then can show all

  const ismyfriend = await User.isMyFriend(idapp, username, myusername);

  let perm = tools.Perm.PERM_NONE;

  if (ismyfriend) {
    perm = tools.Perm.PERM_FRIEND;
  }

  if (username === myusername) {
    perm = tools.Perm.PERM_ALL;
  } else {
    if (await User.canHavePower(myperm) && usaSuperPower) {
      perm = tools.Perm.PERM_ALL;
    }
  }

  let whatToShow = {};

  if (perm === tools.Perm.PERM_NONE) {
    whatToShow = {
      lang: 1,
      index: 1,
      username: 1,
      deleted: 1,
      sospeso: 1,
      verified_email: 1,
      verified_by_aportador: 1,
      date_reg: 1,
      'profile.username_telegram': 1,
      'profile.img': 1,
    };

  } else if (perm === tools.Perm.PERM_FRIEND) {
    whatToShow = {
      lang: 1,
      index: 1,
      username: 1,
      aportador_solidario: 1,
      name: 1,
      surname: 1,
      deleted: 1,
      sospeso: 1,
      verified_email: 1,
      verified_by_aportador: 1,
      'profile.nationality': 1,
      'profile.qualifica': 1,
      'profile.biografia': 1,
      'profile.teleg_id': 1,
      'profile.username_telegram': 1,
      'profile.intcode_cell': 1,
      'profile.cell': 1,
      'profile.website': 1,
      'profile.img': 1,
      'profile.sex': 1,
      'profile.dateofbirth': 1,
      'profile.born_city_id': 1,
      'profile.born_province': 1,
      'profile.born_country': 1,
      email: 1,
      date_reg: 1,
    };

  } else if (perm === tools.Perm.PERM_ALL) {
    whatToShow = {
      lang: 1,
      index: 1,
      username: 1,
      aportador_solidario: 1,
      name: 1,
      surname: 1,
      deleted: 1,
      sospeso: 1,
      verified_email: 1,
      verified_by_aportador: 1,
      notask_verif: 1,
      'profile.nationality': 1,
      'profile.qualifica': 1,
      'profile.biografia': 1,
      'profile.teleg_id': 1,
      'profile.username_telegram': 1,
      'profile.intcode_cell': 1,
      'profile.cell': 1,
      'profile.website': 1,
      'profile.img': 1,
      'profile.sex': 1,
      'profile.dateofbirth': 1,
      'profile.born_city_id': 1,
      'profile.born_province': 1,
      'profile.born_country': 1,
      'mycities': 1,
      'comune': 1,
      email: 1,
      date_reg: 1,
    };
  }

  const myfind = {
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  };

  const query = [
    {$match: myfind},
    {
      $lookup: {
        from: 'cities',
        localField: 'profile.born_city_id',
        foreignField: '_id',
        as: 'mycities',
      },
    },
    {
      '$replaceRoot': {
        'newRoot': {
          '$mergeObjects': [
            {
              '$arrayElemAt': [
                '$mycities',
                0,
              ],
            },
            '$$ROOT',
          ],
        },
      },
    },
    {$project: whatToShow},

  ];

  try {
    const ris = await User.aggregate(query);

    if (ris && ris.length > 0)
      return ris[0];
  } catch (e) {
    return null;
  }

  return null;

  /*
  return User.findOne({
  }, whatToShow).then((rec) => {
    return (rec._doc);
  }).catch((e) => {
    return null;
  });


   */

};

UserSchema.statics.getArrUsernameFromFieldByUsername = async function(
    idapp, username, field, subfield) {

  const myobj = {};
  myobj[field + '.' + subfield] = 1;

  let arrrec = await User.findOne({
    idapp, 'username': username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, myobj).then((ris) => ris ? ris._doc[field][subfield] : []);

  if (arrrec && arrrec.length > 0) {
    return arrrec.map(m => m.username);
  }
  return [];

};

UserSchema.statics.getUsernameReqFriendsByUsername = async function(
    idapp, username) {

  return this.getArrUsernameFromFieldByUsername(idapp, username, 'profile',
      'req_friends');

};

UserSchema.statics.getUsernameFriendsByUsername = async function(
    idapp, username) {

  return this.getArrUsernameFromFieldByUsername(idapp, username, 'profile',
      'friends');

};

UserSchema.statics.getUsernameGroupsByUsername = async function(
    idapp, username) {

  return this.getArrUsernameFromFieldByUsername(idapp, username, 'profile',
      'groups');

};

// Rimuovo l'Amicizia
UserSchema.statics.removeFriend = async function(
    idapp, username, usernameDest) {
  return User.updateOne({idapp, username},
      {$pull: {'profile.friends': {username: {$in: [usernameDest]}}}});
};

// Rimuovo il Gruppo
UserSchema.statics.removeFromMyGroups = async function(
    idapp, username, groupnameDest) {
  return User.updateOne({idapp, username},
      {$pull: {'profile.mygroups': {groupname: {$in: [groupnameDest]}}}});
};

// Rimuovo il Gruppo per Tutti gli Utenti
UserSchema.statics.removeAllUsersFromMyGroups = async function(idapp, groupnameDest) {
  return User.updateMany({idapp},
      {$pull: {'profile.mygroups': {groupname: {$in: [groupnameDest]}}}});
};

// Rimuovo la Richiesta di Amicizia
UserSchema.statics.removeReqFriend = async function(
    idapp, username, usernameDest) {
  return User.updateOne({idapp, username: username},
      {$pull: {'profile.req_friends': {username: {$in: [usernameDest]}}}});
};

UserSchema.statics.setFriendsCmd = async function(idapp, usernameOrig, usernameDest, cmd, value) {

  let ris = null;
  let update = {};
  try {
    if (cmd === shared_consts.FRIENDSCMD.SETTRUST) {

      // Aggiungi alle amicizie
      await this.setFriendsCmd(idapp, usernameOrig, usernameDest,
          shared_consts.FRIENDSCMD.SETFRIEND, value);

      return User.updateOne({idapp, username: usernameDest},
          {$set: {verified_by_aportador: value, trust_modified: new Date()}},
          {new: false});

    } else if (cmd === shared_consts.FRIENDSCMD.SETFRIEND) {
      // Aggiungo l'Amicizia a me
      const foundIfAlreadyFriend = await User.findOne({
        idapp,
        username: usernameOrig,
        'profile.friends': {
          $elemMatch: {username: {$eq: usernameDest}},
        },
      });

      if (!foundIfAlreadyFriend) {
        update = {
          $push: {
            'profile.friends': {
              username: usernameDest,
              date: new Date(),
            },
          },
        };
        ris = await User.updateOne({idapp, username: usernameOrig}, update);

        update = {$pull: {'profile.req_friends': {username: {$in: [usernameDest]}}}};
        ris = await User.updateOne({idapp, username: usernameOrig}, update);
      }

      // Controlla se lui aveva già la mia amicizia
      const foundIfAlreadyFriend2 = await User.findOne({
        idapp,
        username: usernameDest,
        'profile.friends': {
          $elemMatch: {username: {$eq: usernameOrig}},
        },
      });

      if (!foundIfAlreadyFriend2) {
        update = {
          $push: {
            'profile.friends': {
              username: usernameOrig,
              date: new Date(),
            },
          },
        };
        ris = await User.updateOne({idapp, username: usernameDest}, update);

        this.removeReqFriend(idapp, usernameDest, usernameOrig); // Rimuovo l'Amicizia da me
      }
      if (ris) {
        ris = await User.getInfoFriendByUsername(idapp, usernameDest);
      }
    } else if (cmd === shared_consts.FRIENDSCMD.REQFRIEND) {
      // Aggiungo la richiesta di Amicizia a me
      const foundIfAlreadyAskFriend = await User.findOne({
        idapp,
        username: usernameDest,
        'profile.req_friends': {
          $elemMatch: {username: {$eq: usernameOrig}},
        },
      });

      if (value) {
        if (!foundIfAlreadyAskFriend) {
          update = {
            $push: {
              'profile.req_friends': {
                username: usernameOrig,
                date: new Date(),
              },
            },
          };
          ris = await User.updateOne({idapp, username: usernameDest}, update);
        }
        if (ris) {
          // Invia una notifica alla persona
          tools.sendNotificationByUsername(idapp, usernameDest, cmd, true,
              usernameOrig);
        }
      } else {
        if (foundIfAlreadyAskFriend) {
          ris = await this.removeFriend(idapp, usernameDest, usernameOrig); // Rimuovo l'Amicizia da me
        }
      }

      if (ris) {
        ris = await User.getInfoAskFriendByUsername(idapp, usernameDest);
      }

    } else if (cmd === shared_consts.FRIENDSCMD.REMOVE_FROM_MYFRIENDS) {

      await this.removeFriend(idapp, usernameDest, usernameOrig); // Rimuovo l'Amicizia da lui
      ris = await this.removeFriend(idapp, usernameOrig, usernameDest); // Rimuovo l'Amicizia da me

    } else if (cmd === shared_consts.FRIENDSCMD.CANCEL_REQ_FRIEND) {

      await this.removeReqFriend(idapp, usernameDest, usernameOrig); // Rimuovo la Richiesta di Amicizia da lui
      ris = await this.removeFriend(idapp, usernameOrig, usernameDest); // Rimuovo l'Amicizia da me

    } else if (cmd === shared_consts.FRIENDSCMD.BLOCK_USER) {

      await this.removeFriend(idapp, usernameDest, usernameOrig); // Rimuovo l'Amicizia da lui
      await this.removeFriend(idapp, usernameOrig, usernameDest); // Rimuovo l'Amicizia da me

      // Blocco la persona
      ris = await User.updateOne({idapp, username: usernameDest}, {
        $set: {
          blocked: true,
          sospeso: true,
          username_who_block: usernameOrig,
          date_blocked: new Date(),
        },
      });
    }
  } catch (e) {
    console.error('Error: ', e);
  }

  return ris;
};

UserSchema.statics.setGroupsCmd = async function(
    idapp, usernameOrig, groupnameDest, cmd, value) {

  let ris = null;
  let update = {};
  try {
    if (cmd === shared_consts.GROUPSCMD.SETGROUP) {
      // Controllo se è stato già inserito
      const foundIfAlreadyGroup = await User.findOne({
        idapp,
        username: usernameOrig,
        'profile.mygroups': {
          $elemMatch: {groupname: {$eq: groupnameDest}},
        },
      });

      if (!foundIfAlreadyGroup) {
        update = {
          $push: {
            'profile.mygroups': {
              groupname: groupnameDest,
              date: new Date(),
            },
          },
        };
        ris = await User.updateOne({idapp, username: usernameOrig}, update);

        // Elimina la richiesta:
        update = {$pull: {req_users: {username: {$in: [usernameOrig]}}}};
        ris = await MyGroup.updateOne({idapp, groupname: groupnameDest},
            update);
      } else {
        ris = false;
      }

      if (ris) {
        ris = await MyGroup.getInfoGroupByGroupname(idapp, groupnameDest);
      }
    } else if (cmd === shared_consts.GROUPSCMD.REQGROUP) {
      // Aggiungo la richiesta di Amicizia a me
      const foundIfAlreadyAskGroup = await MyGroup.findOne({
        idapp,
        groupname: groupnameDest,
        'req_users': {
          $elemMatch: {username: {$eq: usernameOrig}},
        },
      });

      if (value) {
        if (!foundIfAlreadyAskGroup) {
          update = {
            $push: {
              'req_users': {
                username: usernameOrig,
                date: new Date(),
              },
            },
          };
          ris = await MyGroup.updateOne({idapp, groupname: groupnameDest},
              update);
        }
        if (ris) {
          // Invia una notifica alla persona
          tools.sendNotificationByGroupname(idapp, groupnameDest, cmd, true);
        }
      } else {
        if (foundIfAlreadyAskGroup) {
          ris = await this.removeFromMyGroups(idapp, usernameOrig,
              groupnameDest); // Rimuovo il Gruppo da me
        }
      }

      if (ris) {
        ris = await MyGroup.getInfoGroupByGroupname(idapp, groupnameDest);
      }

    } else if (cmd === shared_consts.GROUPSCMD.REMOVE_FROM_MYGROUP) {

      ris = await User.removeFromMyGroups(idapp, usernameOrig, groupnameDest); // Rimuovo l'Amicizia da me
      console.log('ris', ris);

    } else if (cmd === shared_consts.GROUPSCMD.CANCEL_REQ_GROUP) {

      ris = await MyGroup.removeReqGroup(idapp, usernameOrig, groupnameDest); // Rimuovo l'Amicizia da me

    } else if (cmd === shared_consts.GROUPSCMD.BLOCK_GROUP) {

      await User.removeFromMyGroups(idapp, usernameOrig, groupnameDest); // Rimuovo l'Amicizia da me

      // Blocco il Gruppo
      ris = await MyGroup.updateOne({idapp, groupname: groupnameDest}, {
        $set: {
          blocked: true,
          username_who_block: usernameOrig,
          date_blocked: new Date(),
        },
      });
      //++Todo: Send Notification to Admin and Group's manager
      tools.sendNotificationByGroupname(idapp, groupnameDest, cmd, true);

    }
  } catch (e) {
    console.error('Error: ', e);
  }

  return ris;
};

function getWhatToShow(idapp, username) {
  return {
    username: 1,
    aportador_solidario: 1,
    name: 1,
    surname: 1,
    deleted: 1,
    sospeso: 1,
    verified_email: 1,
    verified_by_aportador: 1,
    notask_verif: 1,
    'profile.nationality': 1,
    'profile.qualifica': 1,
    'profile.biografia': 1,
    'profile.username_telegram': 1,
    'profile.intcode_cell': 1,
    'profile.cell': 1,
    'profile.website': 1,
    'profile.img': 1,
    'profile.sex': 1,
    'profile.dateofbirth': 1,
    'profile.born_city_id': 1,
    'profile.born_province': 1,
    'profile.born_country': 1,
    email: 1,
    date_reg: 1,
  };

}

function getWhatToShow_Unknown(idapp, username) {
  return {
    username: 1,
    aportador_solidario: 1,
    name: 1,
    // deleted: 1,
    // sospeso: 1,
    verified_email: 1,
    verified_by_aportador: 1,
    'profile.username_telegram': 1,
    'profile.img': 1,
    'profile.sex': 1,
    'profile.born_province': 1,
    'profile.born_country': 1,
    date_reg: 1,
  };

}

UserSchema.statics.getWhatToShow_IfFriends = async function(idapp, username) {
  return {
    username: 1,
    aportador_solidario: 1,
    name: 1,
    // deleted: 1,
    // sospeso: 1,
    verified_email: 1,
    'profile.username_telegram': 1,
    verified_by_aportador: 1,
    'profile.img': 1,
    'profile.sex': 1,
    'profile.born_province': 1,
    'profile.born_country': 1,
    date_reg: 1,
    groups: 1,
    friends: 1,
  };

};

UserSchema.statics.getInfoFriendByUsername = async function(idapp, username) {

  const whatToShow = getWhatToShow(idapp, username);

  return User.findOne({
    idapp,
    username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, whatToShow).then((rec) => !!rec ? rec._doc : null);

};

UserSchema.statics.getInfoAskFriendByUsername = async function(
    idapp, username) {

  const whatToShow = getWhatToShow_Unknown(idapp, username);

  return User.findOne({
    idapp,
    username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, whatToShow).then((rec) => !!rec ? rec._doc : null);

};

UserSchema.statics.getAskedFriendsByUsername = async function(idapp, username) {

  const whatToShow_Unknown = getWhatToShow_Unknown(idapp, username);

  return User.find({
    idapp,
    'profile.req_friends': {
      $elemMatch: {username: {$eq: username}},
    },
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, whatToShow_Unknown).then((rec) => {

    //return rec.map(m => m.username);
  });

};

UserSchema.statics.getFriendsByUsername = async function(idapp, username) {

  try {
    const whatToShow = getWhatToShow(idapp, username);
    const whatToShow_Unknown = getWhatToShow_Unknown(idapp, username);
    const arrUsernameFriends = await User.getUsernameFriendsByUsername(idapp,
        username);
    const arrUsernameReqFriends = await User.getUsernameReqFriendsByUsername(
        idapp, username);

    let listFriends = await User.find({
      idapp,
      username: {$in: arrUsernameFriends},
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    }, whatToShow);

    let listRequestFriends = await User.find({
      idapp,
      username: {$in: arrUsernameReqFriends},
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    }, whatToShow_Unknown);

    let listSentRequestFriends = await User.find({
      idapp,
      'profile.req_friends': {
        $elemMatch: {username: {$eq: username}},
      },
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    }, whatToShow_Unknown);

    let listTrusted = await User.find({
      idapp, aportador_solidario: username,
      'profile.teleg_id': {$gt: 0},
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    }, whatToShow);

    return {
      listFriends,
      listRequestFriends,
      listTrusted,
      listSentRequestFriends,
    };

  } catch (e) {
    console.log('Error', e);
  }

  return {
    listFriends: [],
    listRequestFriends: [],
    listTrusted: [],
    listSentRequestFriends: [],

  };
};

UserSchema.statics.getAportadorSolidarioByUsername = async function(
    idapp, username) {
  const User = this;

  return User.findOne({
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).then((rec) => {
    return ((rec) ? rec.aportador_solidario : '');
  }).catch((e) => {
    console.error('getAportadorSolidarioByUsername', e);
  });
};

UserSchema.statics.UserByIdTelegram = async function(idapp, teleg_id) {
  const User = this;

  return User.findOne({
    idapp, 'profile.teleg_id': teleg_id,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).then((rec) => {
    return (!!rec) ? rec._doc : null;
  }).catch((e) => {
    console.error('UserExistByIdTelegram', e);
  });
};

UserSchema.statics.UsersByIdTelegram = async function(idapp, teleg_id) {
  const User = this;

  return User.find({
    idapp, 'profile.teleg_id': teleg_id,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).lean().then((rec) => {
    return (!!rec) ? rec._doc : null;
  }).catch((e) => {
    console.error('UserExistByIdTelegram', e);
  });
};

UserSchema.statics.setPicProfile = async function(idapp, username, imgpic) {
  const User = this;

  const fields_to_update = {
    'profile.img': imgpic,
  };

  return User.findOneAndUpdate({
    idapp, username,
  }, {$set: fields_to_update}, {new: false}).lean().then((record) => {
    return !!record;
  });

};

UserSchema.statics.TelegIdByUsername = async function(idapp, username) {
  const User = this;

  return User.findOne({
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {'profile.teleg_id': 1}).lean().then((rec) => {
    return (!!rec) ? rec.profile.teleg_id : null;
  }).catch((e) => {
    console.error('TelegIdByUsername', e);
  });
};
UserSchema.statics.notAsk_VerifByUsername = async function(idapp, username) {

  return User.findOne({
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {'notask_verif': 1}).lean().then((rec) => {
    return (!!rec && rec.notask_verif) ? true: false;
  }).catch((e) => {
    console.error('notAsk_VerifByUsername', e);
    return false;
  });
};

UserSchema.statics.SetTelegramCheckCode = async function(
    idapp, id, teleg_checkcode) {
  const User = this;

  const fields_to_update = {
    'profile.teleg_checkcode': teleg_checkcode,
  };

  return User.findOneAndUpdate({
    _id: id,
  }, {$set: fields_to_update}, {new: false}).lean().then((record) => {
    return !!record;
  });

};

UserSchema.statics.NonVoglioImbarcarmi = async function(idapp, username) {
  const User = this;

  const fields_to_update = {
    non_voglio_imbarcarmi: true,
  };

  return User.findOneAndUpdate({
    idapp,
    username,
  }, {$set: fields_to_update}, {new: false}).then((record) => {
    return !!record;
  });

};

UserSchema.statics.SetTelegramIdSuccess = async function(idapp, id, teleg_id) {
  const User = this;

  const fields_to_update = {
    'profile.teleg_id': teleg_id,
    'profile.teleg_id_old': 0,
    'profile.teleg_checkcode': 0,
  };

  return User.findOneAndUpdate({
    idapp,
    _id: id,
  }, {$set: fields_to_update}, {new: false}).lean().then((record) => {
    return record;
  });

};

UserSchema.statics.setUsernameTelegram = async function(
    idapp, id, username_telegram, firstname_telegram, lastname_telegram) {
  const User = this;

  const fields_to_update = {
    'profile.username_telegram': username_telegram,
    'profile.firstname_telegram': firstname_telegram,
    'profile.lastname_telegram': lastname_telegram,
  };

  return User.findOneAndUpdate({
    idapp,
    _id: id,
  }, {$set: fields_to_update}, {new: false}).then((record) => {
    return record;
  });

};

UserSchema.statics.getLangByIndOrder = async function(idapp, ind_order) {
  const User = this;

  const rec = await User.getSmallRecByIndOrder(idapp, ind_order);
  return (!!rec) ? rec.lang : '';

};

UserSchema.statics.SetLang = async function(idapp, id, lang) {
  const User = this;

  const fields_to_update = {
    lang,
  };

  return User.findOneAndUpdate({
    _id: id,
  }, {$set: fields_to_update}, {new: false}).then((record) => {
    return record;
  });

};

UserSchema.statics.SetTelegramWasBlocked = async function(idapp, teleg_id) {
  const User = this;

  const fields_to_update = {
    'profile.teleg_id_old': teleg_id,
    'profile.teleg_id': 0,
  };

  if (tools.sulServer()) {

    const ris = await User.findOneAndUpdate({
      idapp,
      'profile.teleg_id': teleg_id,
    }, {$set: fields_to_update}, {new: false}).then((record) => {
      return record;
    });
  }

};

UserSchema.statics.getNameSurnameByUsername = async function(
    idapp, username, reale = false) {
  const User = this;

  return User.findOne({
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {username: 1, name: 1, surname: 1}).then((rec) => {
    let ris = rec.username;
    if (!!rec) {
      if (reale) {
        if (!rec.name)
          return '';

        ris = `${rec.name} ${rec.surname}`;
      } else {
        if (rec.name) {
          ris = `${rec.name} ${rec.surname}`;
        }
      }
    }
    return (!!rec) ? ris : '';
  }).catch((e) => {
    console.error('getNameSurnameByUsername', e);
  });
};

UserSchema.statics.getIdByUsername = async function(idapp, username) {
  const User = this;

  return User.findOne({
    idapp, username,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {username: 1, _id: 1}).then((rec) => {
    return (!!rec) ? rec._id.toString() : '';
  }).catch((e) => {
    console.error('getNameSurnameByUsername', e);
  });
};

UserSchema.statics.getNameSurnameById = async function(idapp, userId) {
  const User = this;

  return User.findOne({
    idapp,
    _id: userId,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }, {name: 1, surname: 1}).then((rec) => {
    return (!!rec) ? `${rec.name} ${rec.surname}` : '';
  }).catch((e) => {
    console.error('getNameSurnameById', e);
  });
};

UserSchema.statics.getSmallRecByIndOrder = async function(idapp, ind_order) {

  try {
    const rec = {};
    /*
    const rec = await ListaIngresso.getarray(idapp,
        {
          idapp,
          ind_order,
          $or: [
            {deleted: {$exists: false}},
            {deleted: {$exists: true, $eq: false}}],
        },
        {
          idapp: 1,
          index: 1,
          ind_order: 1,
          old_order: 1,
          username: 1,
          name: 1,
          lang: 1,
          surname: 1,
          'profile.teleg_id': 1,
        });

    if (!!rec)
      return rec[0];

     */

    return null;

  } catch (e) {
    console.error('getSmallRecByIndOrder', e);
  }

};

UserSchema.statics.NuovoSistema = function(idapp) {
  const User = this;

  try {
    return User.findOne({idapp, old_order: {$exists: true}}).then((ris) => {
      return !!ris;
    });
  } catch (e) {
    console.error('NuovoSistema', e.message);
  }

};

UserSchema.statics.getRecByIndOrder = async function(idapp, ind_order) {
  const User = this;

  try {
    /*
    let filters = {
      idapp: 1,
      index: 1,
      ind_order: 1,
      old_order: 1,
      username: 1,
      email: 1,
      name: 1,
      lang: 1,
      surname: 1,
      'profile.teleg_id': 1,
      'profile.email_paypal': 1,
      'profile.payeer_id': 1,
      'profile.advcash_id': 1,
      'profile.revolut': 1,
      'profile.link_payment': 1,
      'profile.note_payment': 1,
      'profile.paymenttypes': 1,
    };

    const rec = await ListaIngresso.getarray(idapp,
        {
          idapp,
          ind_order,
        },
        filters);

    if (!!rec)
      return rec[0];


     */
    return null;

  } catch (e) {
    console.error('getRecByIndOrder', e);
  }

};

UserSchema.statics.getusersManagers = async function(idapp) {
  const User = this;

  return User.find({idapp, 'profile.manage_telegram': true},
      {username: 1, 'profile.teleg_id': 1, perm: 1}).then((arrrec) => {
    return (!!arrrec) ? arrrec : null;
  }).catch((e) => {
    console.error('getusersManagers', e);
  });
};

UserSchema.statics.getusersRespList = async function(idapp) {
  const User = this;

  return User.find({idapp, 'profile.resplist': true},
      {_id: 1, username: 1, name: 1, surname: 1}).then((arrrec) => {
    return (!!arrrec) ? arrrec : null;
  }).catch((e) => {
    console.error('getusersRespList', e);
  });
};

UserSchema.statics.getusersWorkersList = async function(idapp) {
  const User = this;

  return User.find({idapp, 'profile.workerslist': true},
      {_id: 1, username: 1, name: 1, surname: 1}).then((arrrec) => {
    return (!!arrrec) ? arrrec : null;
  }).catch((e) => {
    console.error('getusersWorkersList', e);
  });
};

UserSchema.statics.getusersManagersAndZoomeri = async function(idapp) {
  const User = this;

  return User.find(
      {
        idapp,
        or: [
          {'profile.manage_telegram': true},
          {
            perm:
                {
                  $bit:

                      Number(shared_consts.Permissions.Zoomeri),

                },
          },
        ],
      },
      {'profile.teleg_id': 1}).then((arrrec) => {
    return (!!arrrec) ? arrrec : null;
  }).catch((e) => {
    console.error('getusersManagers', e);
  });
};

UserSchema.statics.getUsersTelegALL = async function(idapp, username) {
  const User = this;

  if (!!username) {
    return User.find({idapp, username, 'profile.teleg_id': {$gt: 0}})
        .lean()
        then((arrrec) => {
          return (!!arrrec) ? arrrec : null;
        }).
        catch((e) => {
          console.error('getUsersTelegALL', e);
        });
  } else {
    return User.find({idapp, 'profile.teleg_id': {$gt: 0}}).
        lean().
        then((arrrec) => {
          return (!!arrrec) ? arrrec : null;
        }).
        catch((e) => {
          console.error('getUsersTelegALL', e);
        });
  }

};

UserSchema.statics.isManagerByIdTeleg = async function(idapp, idtelegram) {
  const User = this;

  return User.findOne({
    idapp,
    'profile.manage_telegram': true,
    'profile.teleg_id': idtelegram,
  }, {'profile.teleg_id': 1}).then((rec) => {
    return (!!rec && rec.profile.teleg_id === idtelegram);
  }).catch((e) => {
    console.error('getusersManagers', e);
    return false;
  });
};

UserSchema.statics.isAdminByIdTeleg = async function(idapp, idtelegram) {
  const User = this;

  return User.findOne({
    idapp,
    username: 'paoloar77',
    'profile.manage_telegram': true,
    'profile.teleg_id': idtelegram,
  }, {'profile.teleg_id': 1}).then((rec) => {
    return (!!rec && rec.profile.teleg_id === idtelegram);
  }).catch((e) => {
    console.error('getusersManagers', e);
    return false;
  });
};

UserSchema.statics.getUsersList = function(idapp) {
  const User = this;

  return User.find({
        'idapp': idapp,
        $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
      },
      {
        username: 1,
        name: 1,
        surname: 1,
        verified_email: 1,
        verified_by_aportador: 1,
        made_gift: 1,
        perm: 1,
        email: 1,
        date_reg: 1,
        img: 1,
      },
  );
};

/**
 * Query blog posts by user -> paginated results and a total count.
 * @returns {Object} Object -> `{ rows, count }`
 */

UserSchema.statics.getFieldsForSearch = function() {
  return [
    {field: 'username', type: tools.FieldType.string},
    {field: 'name', type: tools.FieldType.string},
    {field: 'index', type: tools.FieldType.number},
    {field: 'ind_order', type: tools.FieldType.number},
    {field: 'old_order', type: tools.FieldType.number},
    {field: 'surname', type: tools.FieldType.string},
    {field: 'email', type: tools.FieldType.string},
    {field: 'profile.cell', type: tools.FieldType.string},
    {field: 'profile.email_paypal', type: tools.FieldType.string},
    {field: 'profile.payeer_id', type: tools.FieldType.string},
    {field: 'profile.advcash_id', type: tools.FieldType.string},
    {field: 'profile.revolut', type: tools.FieldType.string},
    {field: 'profile.link_payment', type: tools.FieldType.string},
    {field: 'profile.teleg_id', type: tools.FieldType.number},
    {field: 'profile.username_telegram', type: tools.FieldType.string},
    {field: 'ipaddr', type: tools.FieldType.string}
    ];
  //{field: 'aportador_solidario', type: tools.FieldType.string}
};

UserSchema.statics.getFieldsForSearchUserFriend = function() {
  return [{field: 'username', type: tools.FieldType.exact}];
};

UserSchema.statics.getFieldsForSearchUserFriend_AllWords = function() {
  return [{field: 'username', type: tools.FieldType.string}];
};

UserSchema.statics.executeQueryTable = function(idapp, params) {
  params.fieldsearch = this.getFieldsForSearch();
  if (params.options) {
    if (tools.isBitActive(params.options,
        shared_consts.OPTIONS_SEARCH_USER_ONLY_FULL_WORDS)) {
      params.fieldsearch = this.getFieldsForSearchUserFriend();
    } else if (tools.isBitActive(params.options,
        shared_consts.OPTIONS_SEARCH_USER_ALL_WORDS)) {
      params.fieldsearch = this.getFieldsForSearchUserFriend_AllWords();
    }
  }
  return tools.executeQueryTable(this, idapp, params);
};

UserSchema.statics.findAllIdApp = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  };

  return User.find(myfind, (err, arrrec) => {
    return arrrec;
  });
};

UserSchema.statics.DuplicateAllRecords = async function(idapporig, idappdest) {

  return tools.DuplicateAllRecords(this, idapporig, idappdest);

};

UserSchema.statics.getDashboard = async function(
    idapp, aportador_solidario, username, aportador_solidario_nome_completo) {
  try {

    // DATA: username, name, surname, email, intcode_cell, cell
    const dashboard = {
      aportador: {},
      arrposizioni: [],
      arrimbarchi: [],
      arrusers: {},
    };

    dashboard.myself = await User.getUserShortDataByUsername(idapp, username);
    // Data of my Aportador
    dashboard.aportador = await User.getUserShortDataByUsername(idapp,
        aportador_solidario);
    // if (dashboard.aportador === undefined) {
    //   dashboard.aportador = await ExtraList.getUserNotRegisteredByNameSurname(idapp, aportador_solidario_nome_completo);
    // }

    const arrap = await User.getDownlineByUsername(idapp, username, false,
        true);
    if (!!arrap)
      dashboard.numpeople_aportador = arrap.length;

    // console.table(dashboard.arrnavi);

    return dashboard;
  } catch (e) {
    console.error(e.message);
    return false;
  }
};

UserSchema.statics.getDownline = async function(
    idapp, aportador_solidario, username) {
  try {

    // DATA: username, name, surname, email, intcode_cell, cell
    let downline = {};

    downline.downline = [];

    // Data of my Downline
    const arrap = await User.getDownlineByUsername(idapp, username);
    if (!!arrap)
      downline.numpeople_aportador = arrap.length;

    downline.downline = await User.getDownlineByUsername(idapp, username, true);

    downline.downbyuser = {};

    for (const down of downline.downline) {
      downline.downbyuser[down.username] = await User.getDownlineByUsername(
          idapp, down.username, false);

      for (const down2 of downline.downbyuser[down.username]) {
        downline.downbyuser[down2.username] = await User.getDownlineByUsername(
            idapp, down2.username, false);
      }
    }

    return downline;
  } catch (e) {
    console.error(e.message);
    return false;
  }
};

/*
UserSchema.statics.fixUsername = async function (idapp, aportador_solidario_ind_order, username) {
  const User = this;

  // Check if somewhere there is my username
  return User.find({ idapp, aportador_solidario_ind_order }, async (err, arrrec) => {
    if (arrrec) {
      for (const myuser of arrrec) {
        if (!myuser.aportador_solidario || myuser.aportador_solidario === tools.APORTADOR_NONE) {
          myuser.aportador_solidario = username;
          await myuser.save()
        }
      }
    }
  });


};
*/

UserSchema.statics.findByCellAndNameSurname = function(
    idapp, cell, name, surname) {
  const User = this;

  return User.findOne({
    'idapp': idapp,
    'profile.cell': cell,
    'name': name,
    'surname': surname,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  });
};

UserSchema.statics.getUsersRegistered = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  };

  return User.count(myfind);
};

/*
UserSchema.statics.getUsersQualified = async function (idapp, numinvitati) {
  const User = this;

  const arrusers = await User.find({
    idapp,
    $and: [
      { $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }] },
      {
        $or: [
          {
            'profile.special_req': true
          },
          {
            verified_email: true,
            'profile.teleg_id': { $gt: 0 },
            'profile.paymenttypes': { "$in": ['paypal'] },
            'profile.saw_and_accepted': shared_consts.ALL_SAW_AND_ACCEPTED,
            'profile.saw_zoom_presentation': true,
            'profile.my_dream': { $exists: true },
            $and: [
              { "$expr": { "$gt": [{ "$strLenCP": "$profile.my_dream" }, 10] } },
              { "$expr": { "$gt": [{ "$strLenCP": "$profile.email_paypal" }, 6] } }
            ],
          }]
      }]

  }, {
    'username': 1,
  });


  if (numinvitati === 0)
    return arrusers;      // PRENDI TUTTI

  let arrris = [];

  for (const rec of arrusers) {
    rec.numinvitatiattivi = await ListaIngresso.getnumInvitatiAttivi(idapp, rec.username);
    if (rec.numinvitatiattivi >= numinvitati) {
      arrris.push(rec);
    }
  }

  return arrris

};
*/

// UserSchema.statics.getNumUsersQualified = async function (idapp, numinvitati) {
//
//   arrrec = await this.getUsersQualified(idapp, numinvitati);
//
//   return arrrec.length
//
// };

UserSchema.statics.getEmailNotVerified = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    verified_email: false,
  };

  return User.count(myfind);
};

UserSchema.statics.getUsersTelegramAttivo = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    'profile.teleg_id': {$gt: 0},
  };

  return User.count(myfind);
};

UserSchema.statics.getUsersTelegramPending = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    'profile.teleg_checkcode': {$gt: 0},
  };

  return User.count(myfind);
};

UserSchema.statics.getNumUsers = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  };

  return User.count(myfind);
};

UserSchema.statics.getUsersZoom = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    'profile.saw_zoom_presentation': true,
  };

  return User.count(myfind);
};

UserSchema.statics.getUsersResidenti = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    'profile.socioresidente': {$exists: true, $eq: true},
  };

  return User.find(myfind, {username: 1, name: 1, surname: 1});
};

UserSchema.statics.getSaw_and_Accepted = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    'profile.saw_and_accepted': shared_consts.ALL_SAW_AND_ACCEPTED,
  };

  return User.count(myfind);
};

UserSchema.statics.getUsersDreams = async function(idapp) {
  const User = this;

  const myfind = {
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
    'profile.my_dream': {$exists: true},
    '$expr': {'$gt': [{'$strLenCP': '$profile.my_dream'}, 10]},
  };

  return User.count(myfind);
};

UserSchema.statics.getLastUsers = async function(idapp) {
  const User = this;

  const lastn = await Settings.getValDbSettings(idapp, 'SHOW_LAST_N_USERS', 5);

  return User.find(
      {
        idapp,
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      },
      {
        username: 1,
        name: 1,
        surname: 1,
        date_reg: 1,
        index: 1,
        'profile.nationality': 1,
      }).sort({date_reg: -1}).limit(lastn).then((arr) => {
    //return JSON.stringify(arr)
    return arr;
  });

};

UserSchema.statics.checkUser = async function(idapp, username) {
  const User = this;

  return User.findOne({idapp, username}, {
    verified_email: 1,
    verified_by_aportador: 1,
    notask_verif: 1,
    'profile.teleg_id': 1,
    'profile.teleg_checkcode': 1,
  }).lean().then((rec) => {
    return rec;
  });

};

UserSchema.statics.calculateStat = async function(idapp, username) {
  const User = this;

  try {
    const {MySkill} = require('../models/myskill');
    const {MyGood} = require('../models/mygood');
    const {MyBacheca} = require('../models/mybacheca');
    const {MyGroup} = require('../models/mygroup');

    const numUsersReg = await User.countDocuments(
        {
          idapp,
          $or: [
            {deleted: {$exists: false}},
            {deleted: {$exists: true, $eq: false}}],
        });

    const numMySkills = await MySkill.countDocuments({idapp});
    const numMyGoods = await MyGood.countDocuments({idapp});

    const numMyBachecas = await MyBacheca.countDocuments({idapp});

    const numGroups = await MyGroup.countDocuments({idapp});

    let numByTab = {}

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

    for (let table of shared_consts.TABLES_VISU_STAT_IN_HOME) {
      let mytable = globalTables.getTableByTableName(table);
      if (mytable) {
        numByTab[table] = await mytable.countDocuments({idapp});
      }
    }

    return {numByTab, numUsersReg};
  } catch (e) {
    console.error(e.message);
  }

};

UserSchema.statics.getDistinctNationalityQuery = function(idapp) {
  const query = [
    {
      $match: {
        idapp,
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      },
    },
    {
      $group: {_id: '$profile.nationality', count: {$sum: 1}},
    },
    {
      $sort: {count: -1},
    },
  ];
  return query;
};

UserSchema.statics.findAllDistinctNationality = async function(idapp) {
  const User = this;

  return User.aggregate(User.getDistinctNationalityQuery(idapp)).then(ris => {
    // console.table(ris);
    return ris;
  });
};

UserSchema.statics.getUsersRegDaily = function(idapp, nrec) {

  const query = [
    {
      $match: {
        idapp,
        date_reg: {$gte: tools.IncDateNow(-(1000 * 60 * 60 * 24 * nrec))},
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      },
    },
    {
      $group: {
        _id: {
          $dateToString: {
            format: '%Y-%m-%d',
            date: '$date_reg',
            timezone: 'Europe/Rome',
          },
        },
        count: {$sum: 1},
      },
    },
    {
      $sort: {_id: 1},
    },
  ];
  return query;
};

UserSchema.statics.getUsersRegWeekly = function(idapp, nrec) {

  const query = [
    {
      $match: {
        idapp,
        date_reg: {$gte: tools.IncDateNow(-(1000 * 60 * 60 * 24 * nrec))},
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      },
    },
    {
      $group: {
        _id: {
          $dateToString: {
            format: '%Y-%U',
            date: '$date_reg',
            timezone: 'Europe/Rome',
          },
        },
        count: {$sum: 1},
      },
    },
    {
      $sort: {_id: 1},
    },
  ];
  return query;
};

UserSchema.statics.getnumRegNDays = function(idapp, nrec) {

  const query = [
    {
      $match: {
        idapp,
        date_reg: {$lt: tools.IncDateNow(-(1000 * 60 * 60 * 24 * nrec))},
        $or: [
          {deleted: {$exists: false}},
          {deleted: {$exists: true, $eq: false}}],
      },
    },
    {
      $group: {
        _id: {
          $dateToString: {
            format: '%Y-%m-%d',
            date: '$date_reg',
            timezone: 'Europe/Rome',
          },
        },
        count: {$sum: 1},
      },
    },
    {
      $sort: {_id: 1},
    },
  ];
  return query;
};

UserSchema.statics.calcnumRegUntilDay = async function(idapp) {
  const User = this;

  return User.aggregate(User.getnumRegNDays(idapp, 30)).then((arr) => {
    return arr.reduce((sum, rec) => sum + rec.count, 0);
  });

};

UserSchema.statics.calcRegDaily = async function(idapp) {
  const User = this;

  return User.aggregate(User.getUsersRegDaily(idapp, 30)).then(ris => {
    // console.table(ris);
    return ris;
  });
};

UserSchema.statics.calcRegWeekly = async function(idapp) {
  const User = this;

  return User.aggregate(User.getUsersRegWeekly(idapp, 20 * 7)).then(ris => {
    // console.table(ris);
    return ris.slice(0, -1);
  });
};

if (tools.INITDB_FIRSTIME) {
  console.log('   createIndex User Index...');
  // UserSchema.index({ username: 'text', name: 'text', surname: 'text', email: 'text' });
  // UserSchema.index({ name: 'name' });
  // UserSchema.index({ name: 1 });
  // UserSchema.index({ surname: 1 });
}

UserSchema.statics.getUsernameByIndOrder = async function(idapp, ind_order) {

  const myrec = await User.getSmallRecByIndOrder(idapp, ind_order);
  return (!!myrec) ? myrec.username : '';

};

UserSchema.statics.getUsernameByIndex = async function(idapp, index) {

  const myrec = await User.findOne({
    idapp,
    index,
  }, {username: 1});

  return (!!myrec) ? myrec.username : '';

};

UserSchema.statics.ricalcolaIndex = async function(idapp) {
  const User = this;

  const arrusers = await User.find({
    idapp,
    $or: [{deleted: {$exists: false}}, {deleted: {$exists: true, $eq: false}}],
  }).sort({old_order: 1});
  let index = 0;
  try {
    for (const user of arrusers) {
      let field = {
        index,
      };

      index++;

      const ris = await User.findOneAndUpdate({_id: user._id}, {$set: field},
          {new: false});
    }
  } catch (e) {
    console.error(e.message);
  }

};

UserSchema.statics.getInfoUser = async function(idapp, username) {
  return {
    username,
    is7req: await User.isUserQualified7(idapp, username),
    // is9req: await User.isUserQualified9(idapp, username),
  };

};

UserSchema.statics.checkIfSbloccatiRequisiti = async function(
    idapp, allData, id) {
  const User = this;

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

  if (!allData.myuser)
    return false;

  //if (await Nave.checkIfNaveExist(idapp, allData.myuser.username)) {
  //  // Se già sei dentro la Nave, allora sei OK
  //  return true;  //TOGLEREE
  //}

  // Controlla se Sblocca i 7 requisiti
  const is7req = await User.isUserQualified7(idapp, allData.myuser.username);
  const is9req = false;
  // const is9req = await User.isUserQualified9(idapp, allData.myuser.username);

  if (!!allData.precDataUser) {
    if ((!allData.precDataUser.is9req && is9req) &&
        !await User.isUserAlreadyQualified_2Invitati(idapp,
            allData.myuser.username)) {
      await User.setUserQualified_2Invitati(idapp, allData.myuser.username);

      // ORA HAI I 9 REQUISITI !
      // const msgtext = telegrambot.getCiao(idapp, allData.myuser.username, allData.myuser.lang) + tools.gettranslate('HAI_I_9_REQUISITI', allData.myuser.lang);
      // telegrambot.sendMsgTelegram(idapp, allData.myuser.username, msgtext, false);  // Anche a STAFF
    }

  }

  // CHECK APORTADOR SOLIDARIO:
  if (!!allData.useraportador) {
    /*
        const is9reqAportador = await User.isUserQualified9(idapp, allData.myuser.aportador_solidario);

        if (!allData.precDataAportador.is9req && is9reqAportador) {
          // ORA HAI I 9 REQUISITI !
          const msgtext = telegrambot.getCiao(idapp, allData.myuser.aportador_solidario, allData.useraportador.lang) + tools.gettranslate('HAI_I_9_REQUISITI', allData.useraportador.lang);
          telegrambot.sendMsgTelegram(idapp, allData.myuser.aportador_solidario, msgtext, true);  // Anche a STAFF
        }
    */
  }

};

UserSchema.statics.mettiSognoePaypal = async function(idapp, modifica) {
  const User = this;

  let num = 0;

  arrusers = await User.find({
    'idapp': idapp,
    $or: [
      {deleted: {$exists: false}},
      {deleted: {$exists: true, $eq: false}},
      {subaccount: {$exists: false}},
      {subaccount: {$exists: true, $eq: false}}],
  });

  for (const rec of arrusers) {

    if (rec.profile.saw_zoom_presentation) {
      aggiornato = false;
      my_dream_ok = !!rec.profile.my_dream;
      if (my_dream_ok)
        my_dream_ok = rec.profile.my_dream.length >= 8;

      if (!my_dream_ok) {
        if (modifica) {
          rec.profile.my_dream = '............';
          aggiornato = true;
        }
        num++;
      }
      revolut_ok = !!rec.profile.revolut;
      paypal_ok = !!rec.profile.email_paypal;

      if (paypal_ok)
        paypal_ok = rec.profile.email_paypal.length > 8;

      if ((!revolut_ok) && (!paypal_ok)) {
        if (modifica) {
          rec.profile.email_paypal = rec.email;
          aggiornato = true;
        }
        num++;
      }

      if (aggiornato && modifica) {

        let allData = {};
        allData.myuser = await User.getUserById(idapp, rec.id);
        if (!!allData.myuser)
          allData.precDataUser = await User.getInfoUser(idapp,
              allData.myuser.username);
        else
          allData.precDataUser = null;

        const risupd = await User.findByIdAndUpdate(rec._id, {
          'profile.email_paypal': rec.profile.email_paypal,
          'profile.my_dream': rec.profile.my_dream,
        }, {new: false});

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

  return {num};

};

UserSchema.statics.convSubAccount = async function(idapp) {
  const User = this;

  // Solo i Cancellati !
  arrusers = await User.find({'idapp': idapp, deleted: true});
  let num = 0;
  for (const rec of arrusers) {
    // Cerca il suo Principale!
    const trovato = await User.findOne({
      idapp,
      'profile.teleg_id': rec.profile.teleg_id,
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    });
    if (trovato) {
      num++;
      await User.findByIdAndUpdate(rec._id, {subaccount: true}, {new: false});
    }
  }

  return {num};
};

UserSchema.statics.getLastRec = async function(idapp) {
  const User = this;

  lastrec = await User.find({idapp}).sort({date_reg: -1}).limit(1).lean();
  if (!!lastrec) {
    return lastrec[0];
  } else {
    return null;
  }
};

UserSchema.statics.DbOp = async function(idapp, mydata) {
  const User = this;
  try {
    if (mydata.dbop === 'changeCellInt') {
      arrusers = await User.find({'idapp': idapp});
      let num = 0;
      for (const rec of arrusers) {
        //  DISATTIVATO: ORA NON MI SERVE PIU
        if (false) {
          let mycell = tools.removespaces(
              rec.profile.intcode_cell + rec.profile.cell);
          await User.findOneAndUpdate({_id: rec._id},
              {$set: {'profile.cell': mycell}});
          num++;
        }
      }

      return {num};

      // return User.updateMany({ idapp }, { $set: { 'profile.cell': { $concat: ["$profile.intcode_cell", "$profile.cell"] } } })
    } else if (mydata.dbop === 'changeEmailLowerCase') {
      arrusers = await User.find({'idapp': idapp});
      let num = 0;
      for (const rec of arrusers) {
        let myemail = rec.email.toLowerCase();
        if (myemail !== rec.email) {
          await User.findOneAndUpdate({_id: rec._id},
              {$set: {'email': myemail}});
          num++;
        }
      }

      return {num};

      /*} else if (mydata.dbop === 'creaLista') {

        await ListaIngresso.deleteMany({ idapp, added: false });

        arrusers = await User.find({
          'idapp': idapp,
          $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }]
        }).sort({ date_added: 1 });
        let num = 0;

        num += await addUtentiInLista(idapp, 1, arrusers);
        num += await addUtentiInLista(idapp, 2, arrusers);
        num += await addUtentiInLista(idapp, 3, arrusers);
        num += await addUtentiInLista(idapp, 4, arrusers);
        num += await addUtentiInLista(idapp, 5, arrusers);
        // num += await addUtentiInLista(idapp, 3);
        // num += await addUtentiInLista(idapp, 4);

        return { num };
        */
    }
  } catch (e) {
    console.error(e.message);

  }

};

UserSchema.statics.addExtraInfo = async function(idapp, recUser) {

  try {
    const listSentMyRequestFriends = await User.find({
      idapp,
      'profile.req_friends': {
        $elemMatch: {username: {$eq: recUser.username}},
      },
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    }, {username: 1}).lean();

    recUser.profile.asked_friends = listSentMyRequestFriends
        ? listSentMyRequestFriends
        : [];

    const listSentMyRequestGroups = await MyGroup.find({
      idapp,
      'req_users': {
        $elemMatch: {username: {$eq: recUser.username}},
      },
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    }, MyGroup.getWhatToShow_Unknown());

    recUser.profile.asked_groups = listSentMyRequestGroups
        ? listSentMyRequestGroups
        : [];

    const listManageGroups = await MyGroup.find({
      idapp,
      'admins': {
        $elemMatch: {username: {$eq: recUser.username}},
      },
      $or: [
        {deleted: {$exists: false}},
        {deleted: {$exists: true, $eq: false}}],
    });

    recUser.profile.manage_mygroups = listManageGroups
        ? listManageGroups
        : [];

  } catch (e) {
    console.error('Err', e);
  }

  return recUser;
};

UserSchema.statics.createNewSubRecord = async function(idapp, req) {
  const User = this;

  // console.log("GENERA TOKEN : ");
  let userId = req.body.data.userId;
  let fieldkey = req.body.data.field;
  let data = req.body.data.data;

  const filtro = {_id: userId};

  let fieldsvalue = {...data};

  fieldsvalue.date_created = new Date();
  fieldsvalue.date_updated = new Date();

  var set = {
    profile: {},
  };
  set.profile[fieldkey] = fieldsvalue;

  const rec = await User.findOneAndUpdate(filtro, {$set: set}, {new: false});

  return rec;
};

const User = mongoose.model('User', UserSchema);

class Hero {
  constructor(name, level) {
    this.name = name;
    this.level = level;
  }

  // Adding a method to the constructor
  greet() {
    return `${this.name} says hello.`;
  }
}

module.exports = {User, Hero};


