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 { Reaction } = require('../models/reaction');

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

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

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

const i18n = require('i18n');

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', false);

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: 3,
    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,
  },
  tokenforgot_code: {
    type: String,
  },
  retry_pwd: {
    type: Number,
    default: 0,
  },
  date_tokenreg: {
    type: Date,
  },
  tokenreg: {
    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,
  },
  reported: {
    type: Boolean,
  },
  username_who_report: {
    type: String,
  },
  date_report: {
    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
    handshake: [
      {
        _id: false,
        username: { type: String },
        date: { type: Date },
      }],  // username
    mygroups: [
      {
        _id: false,
        groupname: { type: String },
        date: { type: Date },
      }],

    mycircuits: [
      {
        _id: false,
        circuitname: { type: String },
        date: { type: Date },
      }],
    last_circuitpath: {
      type: String,
    },
    lastdate_reqRis: {
      type: Date,
    },
    notifs: [
      {
        _id: false,
        dir: { type: Number },
        value: { type: Number },
      },
    ],
    notif_idCities: [
      {
        type: Number,
      }],
    notif_provinces: [
      {
        type: String,
      }],
    notif_regions: [
      {
        type: String,
      }],
    notif_sectors: [
      {
        type: Number,
      }],
    notif_sector_goods: [
      {
        type: Number,
      }],
    resid_prov_id: {
      type: Number,
    },
    resid_province: {
      type: String,
      trim: true,
    },
    resid_card: {
      type: String,
      trim: true,
    },
    stepTutorial: {
      type: Number,
    },
    noNameSurname: {
      type: Boolean,
    },
    noCircuit: {
      type: Boolean,
    },
    noCircIta: {
      type: Boolean,
    },
    noFoto: {
      type: Boolean,
    },
    seen: [
      {
        _id: false,
        id: { type: String },
        tab: { type: Number },
      },
    ],
    bookmark: [
      {
        _id: false,
        id: { type: String },
        tab: { type: Number },
      },
    ],
    favorite: [
      {
        _id: false,
        id: { type: String },
        tab: { type: Number },
      },
    ],
    attend: [
      {
        _id: false,
        id: { type: String },
        tab: { type: Number },
        num: { type: Number },
      },
    ],
    version: { type: Number },
  },
});



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.setOnLine = function (idapp, username) {
  const User = this;

  try {
    return User.findOneAndUpdate({ idapp, username }, { $set: { lasttimeonline: new Date() } });
  } catch (e) {

  }

};


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 await 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.isFacilitatore(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.isFacilitatore = function (perm) {
  try {
    return ((perm & shared_consts.Permissions.Facilitatore) ===
      shared_consts.Permissions.Facilitatore);
  } 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 = '';

  let regexp = new RegExp(`^${username}$`, 'i');

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

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

      }
    }

    return rec;
  });
};

UserSchema.statics.getProjectUser = function () {
  return {
    idapp: 1,
    lang: 1,
    index: 1,
    ind_order: 1,
    username: 1,
    aportador_solidario: 1,
    name: 1,
    surname: 1,
    lasttimeonline: 1,
    deleted: 1,
    reported: 1,
    date_report: 1,
    username_who_report: 1,
    sospeso: 1,
    verified_email: 1,
    verified_by_aportador: 1,
    'profile.teleg_id': 1,
    'profile.username_telegram': 1,
    'profile.firstname_telegram': 1,
    'profile.lastname_telegram': 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,
    tokenreg: 1,
    date_tokenreg: 1,
  };

};

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

  let regexp = new RegExp(`^${username}$`, 'i');

  const myrec = await User.findOne({
    'idapp': idapp,
    username: { $regex: regexp },
    $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
  },
    this.getProjectUser(),
  ).lean();

  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.getUserShortDataByUsernameTelegram = async function (idapp, username_telegram) {
  const User = this;

  let regexp = new RegExp(`^${username_telegram}$`, 'i');

  const myrec = await User.findOne({
    'idapp': idapp,
    'profile.username_telegram': { $regex: regexp },
    $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
  },
    this.getProjectUser(),
  ).lean();

  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();

  let scrivi = true;
  if (userver) {
    scrivi = userver.verified_by_aportador !== valuebool;
  }
  if (scrivi) {
    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 await 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 await User. countDocuments({
    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.findByLinkTokenforgotCode = function (idapp, email, tokenforgot_code) {
  const User = this;

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

UserSchema.statics.createNewRequestPwd = function (idapp, email, code) {
  const User = this;

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

  console.log('createNewRequestPwd');

  if (code && code.length === 6) {
    return User.findByLinkTokenforgotCode(idapp, email, code)
      .then((user) => {
        if (user)
          return { ris: true, link: tools.getlinkRelativeRequestNewPassword(idapp, email, user.tokenforgot) };
        else
          return { ris: false };
      }).catch((e) => {
        console.log(' Err createNewRequestPwd', e.message);
        res.status(400).send();
      });
  } else {
    return User.findByEmail(idapp, email).then(async (user) => {
      if (!user) {
        return { ris: false };
      } else {
        // Creo il tokenforgot
        user.tokenforgot = jwt.sign(user._id.toHexString(), process.env.SIGNCODE).
          toString();
        user.date_tokenforgot = new Date();
        user.tokenforgot_code = 100000 + Math.round(Math.random() * 899999);
        user.lasttimeonline = new Date();
        return await user.save().then(async () => {
          await sendemail.sendEmail_RequestNewPassword(user.lang, user, user.email, user.idapp, user.tokenforgot, user.tokenforgot_code);

          return { ris: true };
        });
      }

    });
  }
};

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

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

  if (user) {
    // Creo il tokenforgot
    user.tokenforgot = jwt.sign(user._id.toHexString(), process.env.SIGNCODE).
      toString();
    user.date_tokenforgot = new Date();
    user.tokenforgot_code = 100000 + Math.round(Math.random() * 899999);
    user.lasttimeonline = new Date();
    user.code_pwd_reset = 0;

    return await user.save().then(() => {
      return tools.getlinkRequestNewPassword(idapp, user.email, user.tokenforgot, user.tokenforgot_code);
    });
  }

  return '';

};

UserSchema.statics.getifRegTokenIsValid = async function (idapp, tokenreg) {
  const User = this;

  let regexp = new RegExp(`^${tokenreg}$`, 'i');

  const user = await User.findOne({
    idapp,
    tokenreg: { $regex: regexp }
  });
  if (user && user.date_tokenreg) {
    return user.date_tokenreg > (new Date().getTime());
  }
  return false;
}


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

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

  if (user) {
    if (true) {
      // Se è scaduto, ne crea uno nuovo
      // Creo il tokenforgot

      if (!user.date_tokenreg || (!user.tokenreg) || (user.tokenreg && (user.date_tokenreg < new Date().getTime()))) {

        let mycodestr = user._id.toHexString() + new Date().getTime().toString();
        user.tokenreg = jwt.sign(mycodestr, process.env.SIGNCODE).
          toString();

        if (user.tokenreg) {
          try {
            user.tokenreg = user.tokenreg.replaceAll('.', '');
            user.tokenreg = user.tokenreg.replaceAll('/', '');
            user.tokenreg = user.tokenreg.slice(-8);
          } catch (e) {
            console.error('err', e);
          }
        }
      }

      user.date_tokenreg = tools.AddDate(new Date(), 1);

      return await user.save().then(() => {
        return user.tokenreg;
      });

    }
  }

  return '';

};

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 (rec && 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.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 await 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 await 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 await myfriends.includes(username);
  } else {
    return false;
  }

};

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

  const myhandshake = await User.getUsernameHandShakeByUsername(idapp, myusername);
  if (myhandshake) {
    return await myhandshake.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,
      aportador_solidario: 1,
      name: 1,
      surname: 1,
      lasttimeonline: 1,
      deleted: 1,
      sospeso: 1,
      reported: 1,
      date_report: 1,
      username_who_report: 1,
      verified_email: 1,
      verified_by_aportador: 1,
      'profile.nationality': 1,
      'profile.mygroups': 1,
      'profile.mycircuits': 1,
      'profile.qualifica': 1,
      'profile.biografia': 1,
      'profile.teleg_id': 1,
      'profile.username_telegram': 1,
      'profile.firstname_telegram': 1,
      'profile.lastname_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,
      'profile.resid_province': 1,
      'profile.resid_card': 1,
      'profile.calc': 1,
      'profile.handshake': 1,
      'profile.friends': 1,
      email: 1,
      date_reg: 1,
      'useraport.username': 1,
      'useraport.profile.img': 1,
    };

  } else if (perm === tools.Perm.PERM_FRIEND) {
    whatToShow = {
      lang: 1,
      index: 1,
      username: 1,
      aportador_solidario: 1,
      name: 1,
      surname: 1,
      lasttimeonline: 1,
      deleted: 1,
      sospeso: 1,
      reported: 1,
      date_report: 1,
      username_who_report: 1,
      verified_email: 1,
      verified_by_aportador: 1,
      'profile.nationality': 1,
      'profile.mygroups': 1,
      'profile.mycircuits': 1,
      'profile.qualifica': 1,
      'profile.biografia': 1,
      'profile.teleg_id': 1,
      'profile.username_telegram': 1,
      'profile.firstname_telegram': 1,
      'profile.lastname_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,
      'profile.resid_province': 1,
      'profile.resid_card': 1,
      'profile.calc': 1,
      'profile.handshake': 1,
      'profile.friends': 1,
      email: 1,
      date_reg: 1,
      'useraport.username': 1,
      'useraport.profile.img': 1,
    };

  } else if (perm === tools.Perm.PERM_ALL) {
    whatToShow = {
      lang: 1,
      index: 1,
      username: 1,
      aportador_solidario: 1,
      name: 1,
      surname: 1,
      lasttimeonline: 1,
      deleted: 1,
      sospeso: 1,
      reported: 1,
      date_report: 1,
      username_who_report: 1,
      verified_email: 1,
      verified_by_aportador: 1,
      notask_verif: 1,
      'profile.nationality': 1,
      'profile.mygroups': 1,
      'profile.mycircuits': 1,
      'profile.qualifica': 1,
      'profile.biografia': 1,
      'profile.teleg_id': 1,
      'profile.username_telegram': 1,
      'profile.firstname_telegram': 1,
      'profile.lastname_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,
      'profile.resid_province': 1,
      'profile.resid_card': 1,
      'profile.calc': 1,
      'profile.handshake': 1,
      'profile.friends': 1,
      'mycities': 1,
      'comune': 1,
      email: 1,
      date_reg: 1,
      'useraport.username': 1,
      'useraport.profile.img': 1,
    };
  }

  let regexpusername = new RegExp(`^${username}$`, 'i');

  const myfind = {
    idapp, username: { $regex: regexpusername },
    $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',
      },
    },
    {
      '$lookup': {
        'from': 'users',
        let: {
          'idapp': '$idapp',
          'user_name': '$aportador_solidario',
        },
        pipeline: [
          {
            '$match': {
              '$expr': {
                $and: [
                  { $eq: ['$username', '$$user_name'] },
                  { $eq: ['$idapp', '$$idapp'] },
                ],
              },
            },
          },
        ],
        as: 'useraport',
      },
    },
    {
      $unwind:
      {
        path: '$useraport',
        preserveNullAndEmptyArrays: true,
      },
    },

    {
      '$replaceRoot': {
        'newRoot': {
          '$mergeObjects': [
            {
              '$arrayElemAt': [
                '$mycities',
                0,
              ],
            },
            '$$ROOT',
          ],
        },
      },
    },
    { $project: whatToShow },

  ];

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

    if (ris && ris.length > 0) {

      ris[0].profile.calc = await User.calcOtherByUser(idapp, ris[0]._id);
      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 await this.getArrUsernameFromFieldByUsername(idapp, username, 'profile',
    'req_friends');

};

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

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

};

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

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

};

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

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

};
UserSchema.statics.getUsernameCircuitsByUsername = async function (
  idapp, username) {

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

};

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

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

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

// Rimuovo il Circuito
UserSchema.statics.removeFromCircuits = async function (idapp, username, circuitname) {

  // Elimina la richiesta (se esiste):
  update = { $pull: { req_users: { username: { $in: [username] } } } };
  await Circuit.updateOne({ idapp, name: circuitname }, update);

  const ris = await User.updateOne({ idapp, username },
    { $pull: { 'profile.mycircuits': { circuitname: { $in: [circuitname] } } } });


  const circuitId = await Circuit.getCircuitIdByName(idapp, circuitname);
  let remove = false;

  // Se il mio account non è stato utilizzato, allora lo cancello anche questo
  const myaccount = await Account.getAccountByUsernameAndCircuitId(idapp, username, circuitId, false, false, '', '');
  if (myaccount && myaccount.totTransato === 0) {
    remove = true;
  } else {
    remove = true;
  }

  if (remove) {
    await Account.removeAccount(myaccount._id);
  }

  return ris;
};

// Aggiungo il Circuito
UserSchema.statics.addCircuitToUser = async function (idapp, usernameOrig, circuitname, confido, groupname, contocom) {

  let ris = null;

  if (groupname) {

    ris = await MyGroup.addCircuitFromGroup(idapp, groupname, circuitname);

    // Elimina la richiesta:
    update = { $pull: { req_groups: { groupname: { $in: [groupname] } } } };
    await Circuit.updateOne({ idapp, name: circuitname }, update);

    // Elimina eventualmente se era bloccato:
    update = { $pull: { refused_groups: { groupname: { $in: [groupname] } } } };
    await Circuit.updateOne({ idapp, name: circuitname }, update);

  } else {

    let update = {
      $push: {
        'profile.mycircuits': {
          circuitname,
          date: new Date(),
        },
      },
    };
    ris = await User.updateOne({ idapp, username: usernameOrig }, update);

    if (confido) {
      // Elimina la richiesta:
      update = { $pull: { req_users: { username: { $in: [usernameOrig] } } } };
      await Circuit.updateOne({ idapp, name: circuitname }, update);
    }

    // Elimina eventualmente se era bloccato:
    update = { $pull: { refused_users: { username: { $in: [usernameOrig] } } } };
    await Circuit.updateOne({ idapp, name: circuitname }, update);

  }

  await Account.createAccount(idapp, usernameOrig, circuitname, confido, groupname, contocom);

  return ris;
};

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

// Rimuovo il Circuito per Tutti gli Utenti
UserSchema.statics.removeAllUsersFromMyCircuits = async function (idapp, circuitname) {
  return await User.updateMany({ idapp },
    { $pull: { 'profile.mycircuits': { circuitname: { $in: [circuitname] } } } });
};

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



// Aggiungo il Partecipa
UserSchema.statics.addAttend = async function (
  req, idapp, username, id, tab, num) {
  const ris = await User.updateOne({ idapp, username },
    { $push: { 'profile.attend': { id, tab, num } } });

  const { SendNotif } = require('../models/sendnotif');

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

  // Invia una Notifica al Destinatario
  const recObjCreator = await globalTables.getUserCreatorByNumTabAndId(idapp, id, tab);

  if (recObjCreator) {
    await SendNotif.createNewNotifToSingleUser(req, null, { usernameDest: recObjCreator.username, recObjCreator, username_action: req.user.username }, false,
      shared_consts.TypeNotifs.TYPEDIR_EVENTS,
      shared_consts.TypeNotifs.ID_EVENTS_ATTEND);

  }

  return ris;
};
// Rimuovo il Bookmark
UserSchema.statics.removeBookmark = async function (
  idapp, username, id, tab) {
  return await User.updateOne({ idapp, username },
    { $pull: { 'profile.bookmark': { id: { $in: [id] }, tab } } });
};

// Aggiungo il Bookmark
UserSchema.statics.addBookmark = async function (
  idapp, username, id, tab) {
  return await User.updateOne({ idapp, username },
    { $push: { 'profile.bookmark': { id, tab } } });
};
// Rimuovo il Partecipa
UserSchema.statics.removeAttend = async function (
  idapp, username, id, tab) {
  return await User.updateOne({ idapp, username },
    { $pull: { 'profile.attend': { id: { $in: [id] }, tab } } });
};

// Aggiungo il Bookmark
UserSchema.statics.addSeen = async function (
  idapp, username, id, tab) {
  return await User.updateOne({ idapp, username },
    { $push: { 'profile.seen': { id, tab } } });
};

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

  const { SendNotif } = require('../models/sendnotif');

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

  if (!req) {
    req = tools.getReqByPar(idapp, usernameOrig);
  }

  const username_action = req.user.username;
  let username_worked = usernameDest;

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

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

      return await 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 } },
        },
      }, { _id: 1 }).lean();

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

        if (!disablenotif) {
          // Send a notification to the DESTINATION FRIENDSHIP !
          let req = tools.getReqByPar(idapp, usernameOrig);
          await SendNotif.createNewNotifToSingleUser(req, null, { usernameDest }, true, shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
            shared_consts.TypeNotifs.ID_FRIENDS_ACCEPTED_MY_REQUEST);

          // Send a notification to the SENDER FRIENDSHIP !
          req = tools.getReqByPar(idapp, usernameDest);
          await SendNotif.createNewNotifToSingleUser(req, null, { usernameDest: usernameOrig }, true, shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
            shared_consts.TypeNotifs.ID_FRIENDS_ACCEPTED);

        }

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

        if (ris) {
          try {
            if (!disablenotif) {
              const userDest = await User.getRecLangAndIdByUsername(idapp, usernameDest);
              const user = await User.getRecLangAndIdByUsername(idapp, usernameOrig);
              const msgDest = i18n.__({ phrase: '✅ %s accepted your Friendship request !', locale: userDest.lang }, usernameDest);
              const msgOrig = i18n.__({ phrase: '✅ You have accepted %s\' Friendship request!', locale: user.lang }, usernameOrig);

              await telegrambot.sendMsgTelegram(idapp, usernameOrig, msgDest);
              await telegrambot.sendMsgTelegram(idapp, usernameDest, msgOrig);
            }
          } catch (e) {
            console.error('Notification : ', e);
          }
        }

      }

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

      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
        this.removeReqFriend(idapp, usernameOrig, usernameDest); // Rimuovo l'Amicizia da te
      }
      //if (ris) {
      ris = await User.getInfoFriendByUsername(idapp, usernameDest);
      //}
    } else if (cmd === shared_consts.FRIENDSCMD.SETHANDSHAKE) {
      // Aggiungo la Stretta di mano a lui
      const foundIfAlreadyHandshake = await User.findOne({
        idapp,
        username: usernameDest,
        'profile.handshake': {
          $elemMatch: { username: { $eq: usernameOrig } },
        },
      }, { _id: 1 }).lean();

      let rec = null;

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

        tools.sendNotificationByUsername(idapp, usernameDest, cmd, true, usernameOrig);
        let req = tools.getReqByPar(idapp, usernameOrig);

        if (rec) {
          try {
            if (!disablenotif) {
              const userDest = await User.getRecLangAndIdByUsername(idapp, usernameDest);
              const user = await User.getRecLangAndIdByUsername(idapp, usernameOrig);
              const already_stretta = await User.isMyHandShake(idapp, usernameDest, usernameOrig);
              const msgDest = i18n.__({ phrase: 'HANDSHAKE_CONFIRMED', locale: userDest.lang }, usernameDest);
              let phrase = 'HANDSHAKE_SET';
              if (already_stretta) {
                phrase = 'HANDSHAKE_ACCEPTED';
              }

              const msgOrig = i18n.__({ phrase, locale: user.lang }, usernameOrig);

              await telegrambot.sendMsgTelegram(idapp, usernameOrig, msgDest);
              await telegrambot.sendMsgTelegram(idapp, usernameDest, msgOrig);
            }
          } catch (e) {
            console.error('Notification : ', e);
          }
        }

        // Send a notification to the DESTINATION HANDSHAKE !
        // await SendNotif.createNewNotifToSingleUser(req, null, { usernameDest }, false, shared_consts.TypeNotifs.TYPEDIR_HANDSHAKE,
        //   shared_consts.TypeNotifs.ID_HANDSHAKE_ACCEPTED);
      }

      const userprofile = await User.getInfoFriendByUsername(idapp, usernameDest);
      const myuser = await User.getInfoFriendByUsername(idapp, usernameOrig);
      ris = { rec, userprofile, myuser };
    } 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 } },
        },
      }, { _id: 1 }).lean();

      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);
        }

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

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

      // Rimuovi anche le eventuali richieste di Amicizia !
      await this.removeReqFriend(idapp, usernameDest, usernameOrig); // Rimuovo la Richiesta di Amicizia da lui
      await this.removeReqFriend(idapp, usernameOrig, usernameDest); // Rimuovo la Richiesta di Amicizia da me

      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.REMOVE_FROM_MYHANDSHAKE) {

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

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

      // CREATE NOTIFICATION IN TABLE SENDNOTIF
      const req = tools.getReqByPar(idapp, usernameOrig);
      await SendNotif.createNewNotifToSingleUser(req, null, { usernameDest }, true, shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
        shared_consts.TypeNotifs.ID_FRIENDS_REFUSED);

      ris = true;

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

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

      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(),
        },
      });
    } else if (cmd === shared_consts.FRIENDSCMD.REPORT_USER) {

      username_worked = usernameDest;

      // Segnalo la persona
      ris = await User.updateOne({ idapp, username: username_worked }, {
        $set: {
          reported: true,
          username_who_report: usernameOrig,
          date_report: new Date(),
        },
      });

      if (ris) {
        // Send a notification to the DESTINATION!
        // Sei stato segnalato da %s per comportamenti non idonei. Contatta %s per chiarimenti
        await SendNotif.createNewNotifToSingleUser(req, null, { username_worked, usernameDest, username_action }, false,
          shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
          shared_consts.TypeNotifs.ID_FRIENDS_REPORTED);

        // Send a notification to the SENDER !
        // Hai segnalato %s da %s per comportamenti non idonei.
        await SendNotif.createNewNotifToSingleUser(req, null, { username_worked, usernameDest: username_action, username_action }, false,
          shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
          shared_consts.TypeNotifs.ID_FRIENDS_REPORTED);

        if (usernameOrig !== telegrambot.ADMIN_USER_SERVER) {
          // Send a notification to the Admin
          await SendNotif.createNewNotifToSingleUser(req, null,
            { username_worked, usernameDest: telegrambot.ADMIN_USER_SERVER, username_action, isAdmin: true }, false,
            shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
            shared_consts.TypeNotifs.ID_FRIENDS_REPORTED);
        }

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

      username_worked = usernameDest;

      // Sblocco la persona
      ris = await User.updateOne({ idapp, username: username_worked }, {
        $set: {
          reported: false,
        },
      });

      if (ris) {
        // Send a notification to the DESTINATION!
        await SendNotif.createNewNotifToSingleUser(req, null, { username_worked, usernameDest, username_action }, false,
          shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
          shared_consts.TypeNotifs.ID_FRIENDS_UNBLOCKED);

        // Send a notification to the SENDER !
        // Hai segnalato %s da %s per comportamenti non idonei.
        await SendNotif.createNewNotifToSingleUser(req, null, { username_worked, usernameDest: username_action, username_action }, false,
          shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
          shared_consts.TypeNotifs.ID_FRIENDS_UNBLOCKED);

        if (usernameOrig !== telegrambot.ADMIN_USER_SERVER) {
          // Send a notification to the Admin
          await SendNotif.createNewNotifToSingleUser(req, null,
            { username_worked, usernameDest: telegrambot.ADMIN_USER_SERVER, username_action, isAdmin: true }, false,
            shared_consts.TypeNotifs.TYPEDIR_FRIENDS,
            shared_consts.TypeNotifs.ID_FRIENDS_UNBLOCKED);
        }

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

  return ris;
};

UserSchema.statics.sendCmd = async function (req, idapp, usernameOrig, usernameDest, cmd, value, disablenotif) {

  const { SendNotif } = require('../models/sendnotif');

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

  const cl = telegrambot.getclTelegByidapp(idapp);

  if (!req) {
    req = tools.getReqByPar(idapp, usernameOrig);
  }

  const myuser = await User.getUserByUsername(idapp, usernameOrig);
  const recuserDest = await User.getUserByUsername(idapp, usernameDest);

  const langdest = recuserDest.lang;
  const telegid = recuserDest.profile.teleg_id;

  let userId = recuserDest._id;
  let title = tools.getNomeAppByIdApp(idapp);
  let keyb = null;
  let descr = '';
  
  let send_notif = false;
  let send_msgTelegramBot = false;
  let actions = [];
  let popupOnApp = '';

  let ris = null;

  try {
    if (cmd === shared_consts.CallFunz.ENTRA_RIS_ITALIA) {
      mycircuitOrig = await Circuit.getCircuitMyProvince(idapp, usernameOrig);
      descr = i18n.__({ phrase: 'SENDMSG_ENTRA_IN_RISO_ITALIA', locale: langdest }, usernameDest, usernameOrig, mycircuitOrig);
      msgtelegram = descr;

      openUrl = '/circuit/ris_italia';
      bottone = i18n.__({ phrase: 'CIRCUIT_OPEN_RISITALIA', locale: langdest });
      tag = 'risitalia';
      
      send_notif = true;
      send_msgTelegramBot = true;

      keyb = cl.getInlineKeyboard(langdest, [
        {
          text: bottone,
          url: tools.getHostByIdApp(idapp) + openUrl,
          // callback_data: InlineConferma.RISPOSTA_SI + myfunc + tools.SEP + myuser.username + tools.SEP + '' + tools.SEP + '' + tools.SEP +
          //   groupid,
        },
      ]);

      popupOnApp = 'Messaggio inviato al destinatario';

    }

    if (send_notif) {
      // SEND PUSH NOTIFICATION
      await tools.sendNotificationToUser(userId, title, descr, openUrl, '', tag, actions);
    }

    // Invia Msg
    if (send_msgTelegramBot && msgtelegram) {      
      await telegrambot.local_sendMsgTelegramByIdTelegram(idapp, telegid, msgtelegram, undefined, undefined, true, keyb);
    }

    const userprofile = await User.getInfoFriendByUsername(idapp, usernameDest);
    const myuser = await User.getInfoFriendByUsername(idapp, usernameOrig);
    
    ris = { userprofile, myuser, popupOnApp, result: true };
    
  } catch (e) {
    popupOnApp = e;
    ris = { popupOnApp };
    console.error('Error sendCmd: ', e);
  }

  return ris;
};

UserSchema.statics.ifAlreadyInGroup = async function (idapp, usernameOrig, groupnameDest) {

  // Controllo se è stato già inserito
  return await User.findOne({
    idapp,
    username: usernameOrig,
    'profile.mygroups': {
      $elemMatch: { groupname: { $eq: groupnameDest } },
    },
  }).lean();

};

UserSchema.statics.ifAlreadyInCircuit = async function (idapp, usernameOrig, circuitname) {

  // Controllo se è stato già inserito
  return await User.findOne({
    idapp,
    username: usernameOrig,
    'profile.mycircuits': {
      $elemMatch: { circuitname: { $eq: circuitname } },
    },
  }).lean();
};

UserSchema.statics.countUsersInGroup = async function (idapp, groupnameDest) {

  // Controllo se è stato già inserito
  return await User.countDocuments({
    idapp,
    'profile.mygroups': {
      $elemMatch: { groupname: { $eq: groupnameDest } },
    },
  });

};

UserSchema.statics.countUsersInCircuit = async function (idapp, circuitname) {

  // Controllo se è stato già inserito
  return await User.countDocuments({
    idapp,
    'profile.mycircuits': {
      $elemMatch: { circuitname: { $eq: circuitname } },
    },
  });

};

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

  let ris = null;
  let update = {};
  try {
    if (cmd === shared_consts.GROUPSCMD.SETGROUP) {
      const foundIfAlreadyGroup = await this.ifAlreadyInGroup(idapp, usernameOrig, 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] } } } };
        await MyGroup.updateOne({ idapp, groupname: groupnameDest }, update);

        // Elimina eventualmente se era bloccato:
        update = { $pull: { refused_users: { username: { $in: [usernameOrig] } } } };
        await MyGroup.updateOne({ idapp, groupname: groupnameDest }, update);
      } else {
        ris = false;
      }
      if (ris) {
        // Invia una notifica alla persona e agli Admin
        tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, value, true, username_action);
        ris = await MyGroup.getInfoGroupByGroupname(idapp, groupnameDest);
      }
    } else if (cmd === shared_consts.GROUPSCMD.REQGROUP) {
      // Aggiungo la richiesta di Gruppo 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
          await tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, true, true, username_action);
        }
      } else {
        if (foundIfAlreadyAskGroup) {
          ris = await this.removeFromMyGroups(idapp, usernameOrig, groupnameDest); // Rimuovo il Gruppo da me

          // Invia una notifica alla persona
          await tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, false, true, username_action);
        }
      }

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

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

      // Remove if is also an Admin
      await MyGroup.removeAdminOfMyGroup(idapp, usernameOrig, groupnameDest);

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

      // Invia una notifica alla persona
      await tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, false, true, username_action);

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

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

      if (ris) {
        // Invia una notifica alla persona e agli Admin
        await tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, false, true, username_action);

      }
      ris = await MyGroup.deleteGroup(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.REFUSE_REQ_GROUP) {

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

      ris = await MyGroup.refuseReqGroup(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, usernameOrig, groupnameDest, cmd, false, true, username_action);

    } else if (cmd === shared_consts.GROUPSCMD.ADDADMIN_OFMYGROUP) {
      ris = await MyGroup.addToAdminOfMyGroup(idapp, usernameOrig, groupnameDest); // Rimuovo la richiesta di entrare nel gruppo

      // Invia una notifica alla persona
      await tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, false, true, username_action);

    } else if (cmd === shared_consts.GROUPSCMD.REMOVEADMIN_OFMYGROUP) {
      ris = await MyGroup.removeAdminOfMyGroup(idapp, usernameOrig, groupnameDest); // Rimuovo la richiesta di entrare nel gruppo

      // Invia una notifica alla persona
      await tools.sendNotificationByGroupname(idapp, usernameOrig, groupnameDest, cmd, false, true, username_action);

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

  return ris;
};

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

  try {
    //++Todo: Ottimizzare ! Non occorre inviare tutti questi dati !!! Solo per il Circuito ?!
    const userprofile = await User.getExtraInfoByUsername(idapp, username);
    if (userprofile) {
      outres.userprofile = userprofile;
    }
    outres.listcircuits = await Circuit.findAllIdApp(idapp);
    outres.mygroups = await MyGroup.findAllGroups(idapp);

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

  return outres;

};


UserSchema.statics.setCircuitCmd = async function (idapp, usernameOrig, circuitname, cmd, value, username_action, extrarec) {

  // console.log('setCircuitCmd', cmd);

  const { SendNotif } = require('../models/sendnotif');

  let ris = null;
  let outres = {
    result: false,
  };
  let update = {};

  let groupname = extrarec && extrarec.groupname ? extrarec.groupname : '';

  try {
    if (cmd === shared_consts.CIRCUITCMD.CREATE) {
      const mycirc = await Circuit.findOne({ idapp, name: circuitname });
      if (mycirc) {
        // Il Conto Comunitario prende il nome del circuito !
        await Account.createAccount(idapp, '', circuitname, true, '', mycirc.path);
      }

    } else if (cmd === shared_consts.CIRCUITCMD.SET) {
      if (groupname) {
        const foundIfCircuitInGroup = await MyGroup.ifCircuitAlreadyInGroup(idapp, groupname, circuitname);

        if (!foundIfCircuitInGroup) {
          ris = await this.addCircuitToUser(idapp, usernameOrig, circuitname, true, groupname);

        } else {
          ris = false;
        }

        await Circuit.updateData(idapp, circuitname)
        if (ris) {
          // Invia una notifica alla persona e agli Admin
          tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, value, true, username_action, extrarec);
          outres.result = await Circuit.getInfoCircuitByName(idapp, circuitname);
        }
      } else {
        const foundIfAlreadyCircuit = await this.ifAlreadyInCircuit(idapp, usernameOrig, circuitname);

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

          // Elimina eventualmente se era bloccato:
          update = { $pull: { refused_users: { username: { $in: [usernameOrig] } } } };
          await Circuit.updateOne({ idapp, name: circuitname }, update);

          await Account.createAccount(idapp, usernameOrig, circuitname);


        } else {
          ris = false;
        }

        await Circuit.updateData(idapp, circuitname)
        if (ris) {
          // Invia una notifica alla persona e agli Admin
          tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, value, true, username_action, extrarec);
          outres.result = await Circuit.getInfoCircuitByName(idapp, circuitname);
        }
      }

    } else if (cmd === shared_consts.CIRCUITCMD.SETFIDO) {

      ris = await Circuit.setFido(idapp, usernameOrig, circuitname, groupname);
      if (ris) {

        extrarec.fidoConcesso = ris.fidoConcesso;
        extrarec.qta_maxConcessa = ris.qta_maxConcessa;

        // Elimina la richiesta:
        update = { $pull: { req_users: { username: { $in: [usernameOrig] } } } };
        await Circuit.updateOne({ idapp, name: circuitname }, update);

        await Circuit.updateData(idapp, circuitname)
        if (ris) {
          // Invia una notifica alla persona e agli Admin
          tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, value, true, username_action, extrarec);
          outres.result = await Circuit.getInfoCircuitByName(idapp, circuitname);
        }
      } else {
        // errore !?
      }

    } else if (cmd === shared_consts.CIRCUITCMD.REQ) {

      if (groupname) {
        // Aggiungo la richiesta di Gruppo a me
        const foundIfAlreadyAskCircuit = await Circuit.findOne({
          idapp,
          name: circuitname,
          'req_groups': {
            $elemMatch: { groupname: { $eq: groupname } },
          },
        });

        if (value) {

          ris = await this.addCircuitToUser(idapp, usernameOrig, circuitname, false, groupname, '');

          if (!foundIfAlreadyAskCircuit) {
            update = {
              $push: {
                'req_groups': {
                  groupname,
                  date: new Date(),
                },
              },
            };
            ris = await Circuit.updateOne({ idapp, name: circuitname }, update);

          }
          if (ris) {
            // Invia una notifica alla persona
            await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, true, true, username_action, extrarec);
          }
        } else {
          if (foundIfAlreadyAskCircuit) {
            outres.result = await MyGroup.removeCircuitFromGroup(idapp, groupname, circuitname); // Rimuovo il Circuito dal gruppo

            // Invia una notifica alla persona
            await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);
          }
        }

      } else {
        // Aggiungo la richiesta di Circuito a me
        const foundIfAlreadyAskCircuit = await Circuit.findOne({
          idapp,
          name: circuitname,
          'req_users': {
            $elemMatch: { username: { $eq: usernameOrig } },
          },
        });

        if (value) {
          // Aggiungi intanto l'utente al Circuito (senza fido)
          ris = await this.addCircuitToUser(idapp, usernameOrig, circuitname, false);

          if (!foundIfAlreadyAskCircuit) {
            update = {
              $push: {
                'req_users': {
                  username: usernameOrig,
                  date: new Date(),
                },
              },
            };
            // Aggiungi la richiesta per ottenere il fido
            ris = await Circuit.updateOne({ idapp, name: circuitname }, update);

          }
          if (ris) {
            // Invia una notifica alla persona
            await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, true, true, username_action, extrarec);
          }
        } else {
          if (foundIfAlreadyAskCircuit) {
            outres.result = await this.removeFromCircuits(idapp, usernameOrig, circuitname); // Rimuovo il Gruppo da me

            // Invia una notifica alla persona
            await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);
          }
        }

      }


    } else if (cmd === shared_consts.CIRCUITCMD.REMOVE_FROM_MYLIST) {

      if (groupname) {
        // Elimina la richiesta:
        update = { $pull: { req_groups: { groupname: { $in: [groupname] } } } };
        await Circuit.updateOne({ idapp, name: circuitname }, update);

        outres.result = await MyGroup.removeCircuitFromGroup(idapp, groupname, circuitname); // Rimuovo l'Amicizia da me
      } else {

        // Remove if is also an Admin
        await Circuit.removeAdminOfMyCircuit(idapp, usernameOrig, circuitname);

        outres.result = await this.removeFromCircuits(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me
        console.log('ris', ris);

        await Circuit.updateData(idapp, circuitname);
      }

      // Invia una notifica alla persona
      await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);

    } else if (cmd === shared_consts.CIRCUITCMD.DELETE) {

      ris = await this.removeFromCircuits(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me

      if (ris) {
        // Invia una notifica alla persona e agli Admin
        await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);

      }
      outres.result = await Circuit.deleteCircuit(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me

    } else if (cmd === shared_consts.CIRCUITCMD.CANCEL_REQ) {

      if (groupname)
        outres.result = await Circuit.removeReqGroupCircuit(idapp, groupname, circuitname); // Rimuovo l'Amicizia da me
      else
        outres.result = await Circuit.removeReqCircuit(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me

      await Circuit.updateData(idapp, circuitname)

    } else if (cmd === shared_consts.CIRCUITCMD.REFUSE_REQ) {

      if (groupname) {
        outres.result = await MyGroup.removeCircuitFromGroup(idapp, groupname, circuitname); // Rimuovo l'Amicizia da me
        outres.result = await Circuit.removeReqGroupCircuit(idapp, groupname, circuitname); // Rimuovo l'Amicizia da me
        outres.result = await Circuit.refuseReqGroupCircuit(idapp, groupname, circuitname); // Rimuovo l'Amicizia da me
      } else {
        outres.result = await this.removeFromCircuits(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me
        outres.result = await Circuit.removeReqCircuit(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me
        outres.result = await Circuit.refuseReqCircuit(idapp, usernameOrig, circuitname); // Rimuovo l'Amicizia da me
      }

      // Invia una notifica alla persona
      await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);

    } else if (cmd === shared_consts.CIRCUITCMD.ADDADMIN) {
      outres.result = await Circuit.addToAdminOfMyCircuit(idapp, usernameOrig, circuitname); // Rimuovo la richiesta di entrare nel gruppo

      // Invia una notifica alla persona
      await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);

    } else if (cmd === shared_consts.CIRCUITCMD.REMOVEADMIN) {
      outres.result = await Circuit.removeAdminOfMyCircuit(idapp, usernameOrig, circuitname); // Rimuovo la richiesta di entrare nel gruppo

      // Invia una notifica alla persona
      await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);

    } else if (cmd === shared_consts.CIRCUITCMD.SENDCOINS_REQ) {

      outres = await Circuit.sendCoins(true, idapp, usernameOrig, extrarec);

      if (outres.cansend) {
        // Invia una notifica di moneta alla persona
        const out = await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action,
          extrarec);
        if (out)
          outres.result = out.ris;
      } else {
        outres.cansend = false;
      }

      ris = true;
    } else if ((cmd === shared_consts.CIRCUITCMD.SENDCOINS_ACCEPT) || (cmd === shared_consts.CIRCUITCMD.SENDCOINS_REFUSE)) {
      // Before to accept, I see if it's already set !

      outres = {
        cansend: false,
        errormsg: '',
      };

      let outcheck = outres;

      let risStatus = '';
      const status = await SendNotif.getStatus(extrarec.notifId);
      if (status === shared_consts.CircuitsNotif.STATUS_ACCEPTED) {
        risStatus = i18n.__('STATUS_SENT');
      } else if (status === shared_consts.CircuitsNotif.STATUS_REFUSED) {
        risStatus = i18n.__('STATUS_REFUSED');
      }
      // if (!await SendNotif.checkIfCoinsAlreadySent(extrarec.notifId)) {
      if (!await Movement.checkIfCoinsAlreadySent(extrarec.notifId)) {
        if (cmd === shared_consts.CIRCUITCMD.SENDCOINS_ACCEPT) {
          outcheck = await Circuit.sendCoins(true, idapp, usernameOrig, extrarec);
        } else {
          outcheck.cansend = true;
          outres.cansend = true;
        }

        if (cmd === shared_consts.CIRCUITCMD.SENDCOINS_ACCEPT && outcheck.cansend) {
          if (!await Movement.checkIfCoinsAlreadySent(extrarec.notifId)) {
            outres = await Circuit.sendCoins(false, idapp, usernameOrig, extrarec);

          } else {
            outcheck.cansend = false; //GIA INVIATO
          }
        }

        if (outcheck.cansend) {
          // Invia una notifica di moneta (accettata o rifiutata) alla persona
          const out = await tools.sendNotificationByCircuit(idapp, usernameOrig, circuitname, cmd, false, true, username_action, extrarec);

          if (outres && extrarec.groupname) {
            // Setta agli altri admin,
          }
        }

        outres.recnotif = await SendNotif.getRecNotif(extrarec.notifId);
        outres.arrrecnotif = await SendNotif.findAllNotifByUsernameIdAndIdApp(username_action, extrarec.lastdr, idapp, shared_consts.LIMIT_NOTIF_FOR_USER);

      } else {
        outres.cansend = false;

        outres.errormsg = i18n.__('CIRCUIT_COINS_ALREADY_PROCESSED', risStatus);
      }
      ris = true;
    }

    if (ris && username_action) {
      outres = await this.updateMyData(outres, idapp, username_action);
    }

    if (circuitname)
      outres.circuit = await Circuit.getInfoCircuitByName(idapp, circuitname);

    return outres;

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

  return ris;
};

function getWhatToShow(idapp, username) {
  return {
    username: 1,
    aportador_solidario: 1,
    name: 1,
    surname: 1,
    lasttimeonline: 1,
    deleted: 1,
    sospeso: 1,
    reported: 1,
    date_report: 1,
    username_who_report: 1,
    verified_email: 1,
    verified_by_aportador: 1,
    notask_verif: 1,
    'profile.nationality': 1,
    'profile.mygroups': 1,
    'profile.mycircuits': 1,
    'profile.qualifica': 1,
    'profile.biografia': 1,
    'profile.username_telegram': 1,
    'profile.firstname_telegram': 1,
    'profile.lastname_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,
    'profile.resid_province': 1,
    'profile.resid_card': 1,
    'profile.calc': 1,
    email: 1,
    date_reg: 1,
    'profile.friends': 1,
    'profile.handshake': 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,
    'profile.resid_province': 1,
    'profile.resid_card': 1,
    'profile.calc': 1,
    date_reg: 1,
    'profile.handshake': 1,
    'profile.friends': 1,
    'profile.favorite': 1,
    'profile.bookmark': 1,
    'profile.attend': 1,
    'profile.seen': 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,
    'profile.resid_province': 1,
    'profile.resid_card': 1,
    'profile.calc': 1,
    reported: 1,
    date_report: 1,
    username_who_report: 1,
    date_reg: 1,
    groups: 1,
    'profile.handshake': 1,
    'profile.friends': 1,
    'profile.favorite': 1,
    'profile.bookmark': 1,
    'profile.attend': 1,
    'profile.seen': 1,
  };

};

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

  const whatToShow = getWhatToShow(idapp, username);

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

};

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

  const whatToShow = getWhatToShow_Unknown(idapp, username);

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

};

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

  const whatToShow_Unknown = getWhatToShow_Unknown(idapp, username);

  return await 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) {

  if (!username) {
    return {
      listFriends: [],
      listRequestFriends: [],
      listTrusted: [],
      listSentRequestFriends: [],
    }
  }

  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);
    const arrUsernameHandShake = await User.getUsernameHandShakeByUsername(idapp,
      username);

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

    let listHandShake = await User.find({
      idapp,
      username: { $in: arrUsernameHandShake },
      $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,
      listHandShake,
      listRequestFriends,
      listTrusted,
      listSentRequestFriends,
    };

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

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

  };
};

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

  return await 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 await User.findOne({
    idapp,
    'profile.teleg_id': teleg_id,
    $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
  }).lean().then(async (rec) => {
    if (!rec) {
      // Cerca se esiste in quello salvato in precedenza:
      const recold = await User.findOne({
        idapp,
        'profile.teleg_id_old': teleg_id
      }).lean();
      if (recold && recold.profile.teleg_id_old === teleg_id) {
        // Riaggiorna l'ID perché è ritornato sulla chat!
        await User.SetTelegramIdSuccess(idapp, recold._id, recold.profile.teleg_id_old);

        rec = await User.findOne({
          idapp,
          'profile.teleg_id': teleg_id,
          $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
        }).lean();
      }

    }
    return (!!rec) ? rec : 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 await 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 await 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.TelegIdById = async function (idapp, id) {
  const User = this;

  return await User.findOne({
    idapp,
    _id: id,
    $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 await 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 await 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 await 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 await User.findOneAndUpdate({
    idapp,
    _id: id,
  }, { $set: fields_to_update }, { new: false }).lean().then((record) => {

    if (record) {
      return User.findOne({
        idapp,
        _id: id,
      }).lean();
    }
  });

  return null;

};

UserSchema.statics.getUsernameTelegram = async function (idapp, username) {
  const User = this;
  return await User.findOne({ idapp, username }, { 'profile.username_telegram': 1 }).lean().then((ris) => {
    if (ris)
      return ris.profile.username_telegram;
    else
      return '';
  });
};

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 await User.findOneAndUpdate({
    idapp,
    _id: id,
  }, { $set: fields_to_update }, { new: false }).then((record) => {
    return record;
  });

};

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

  const fields_to_update = {
    lang,
  };

  return await 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()) {
  if (true) {

    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 await 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;

  let regexp = new RegExp(`^${username}$`, 'i');

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

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

  let regexp = new RegExp(`^${username}$`, 'i');

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

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

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

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

  return await 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.getusersManagers = async function (idapp) {
  const User = this;
  // Int32 mongodb 6.0
  return await User.find({ idapp, 'profile.manage_telegram': true, perm: { $bitsAnySet: 0b010 } },
    { username: 1, 'profile.teleg_id': 1, perm: 1 }).then((arrrec) => {
      return (!!arrrec) ? arrrec : null;
    }).catch((e) => {
      console.error('getusersManagers', e);
    });
};

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

  // Int32 mongodb 6.0

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

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

  return await 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 await 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 await 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 await User.find({ idapp, username, 'profile.teleg_id': { $gt: 0 } }).lean();
    then((arrrec) => {
      return (!!arrrec) ? arrrec : null;
    }).
      catch((e) => {
        console.error('getUsersTelegALL', e);
      });
  } else {
    return await 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 await 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('isManagerByIdTeleg', e);
    return false;
  });
};

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

  return await 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,
      lasttimeonline: 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 },
    { field: 'profile.username_telegram', type: tools.FieldType.string },
    { field: 'name', type: tools.FieldType.string },
    { field: 'surname', type: tools.FieldType.string },
  ];
};

UserSchema.statics.getFieldsForSearchUserFriend_AllWords = function () {
  return [
    { field: 'username', type: tools.FieldType.string },
    { field: 'profile.username_telegram', type: tools.FieldType.string },
    { field: 'name', type: tools.FieldType.string },
    { field: 'surname', 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 await User.find(myfind, (err, arrrec) => {
    return arrrec;
  });
};

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

  return await 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 await User.countDocuments(myfind);
};

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

  let starttoday = new Date();
  starttoday.setHours(0, 0, 0, 0);

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

  return await User.countDocuments(myfind);
};

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

  let starttoday = new Date();
  starttoday.setHours(0, 0, 0, 0);

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

  return await User.countDocuments(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 await User.countDocuments(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 await User.countDocuments(myfind);
};

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

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

  return await User.countDocuments(myfind);
};

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

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

  return await User.countDocuments(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 await User.countDocuments(myfind);
};

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

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

  return await User.countDocuments(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 await User.countDocuments(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 await 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 await User.countDocuments(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 await User.countDocuments(myfind);
};

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

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

  return await User.find(
    {
      idapp,
      $or: [
        { deleted: { $exists: false } },
        { deleted: { $exists: true, $eq: false } }],
    },
    {
      username: 1,
      name: 1,
      surname: 1,
      lasttimeonline: 1,
      'profile.img': 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.getLastOnlineUsers = async function (idapp) {
  const User = this;

  const lastn = await Settings.getValDbSettings(idapp, 'SHOW_LAST_ONLINE_USERS', 20);

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

};

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

  const lastn = 10;

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

};

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

  const lastn = 10;

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

};

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

  const lastn = 10;

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

};

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

  return await User.aggregate(User.getQueryReceiveRISUsers(idapp, 8)).then(ris => {
    // console.table(ris);
    return ris;
  });

};

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

  return await 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 globalTables = require('../tools/globalTables');

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

    let numByTab = {};

    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 await 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.getQueryUsersDiffusori = function (idapp) {

  const query = [
    {
      $match: {
        idapp,
        $or: [
          {
            deleted: {
              $exists: false,
            },
          },
          {
            deleted: {
              $exists: true,
              $eq: false,
            },
          },
        ],
      },
    },
    {
      $group: {
        _id: "$aportador_solidario",
        count: {
          $sum: 1,
        },
      },
    },
    {
      $match: { "count": { $gte: 2 } }
    },
    {
      $sort: {
        count: -1,
      },
    },
    { $limit: 20 },
    {
      $lookup: {
        from: "users",
        let: {
          username: "$_id",
          idapp,
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  {
                    $eq: [
                      "$$username",
                      "$username",
                    ],
                  },
                  {
                    $eq: [
                      "$$idapp",
                      "$idapp",
                    ],
                  },
                ],
              },
            },
          },
        ],
        as: "user",
      },
    },
    { $unwind: "$user" },
    {
      $replaceRoot: {
        newRoot: {
          $mergeObjects: ["$user", "$$ROOT"],
        },
      },
    },
    {
      $project: {
        _id: 0,
        count: 1,
        aportador_solidario: 1,
        username: 1,
        name: 1,
        surname: 1,
        lasttimeonline: 1,
        date_reg: 1,
        idapp: 1,
        "profile.img": 1,
        'profile.mycircuits': 1,
      },
    },
  ];
  return query;
};

UserSchema.statics.getQueryUsersStretteDiMano = function (idapp) {

  const query = [
    {
      $match: {
        idapp: "13",
        'profile.handshake': { $exists: true },
        $or: [
          {
            deleted: {
              $exists: false,
            },
          },
          {
            deleted: {
              $exists: true,
              $eq: false,
            },
          },
        ],
      },
    },

    {
      $group:
      {
        _id: "$username",
        count:
          { $sum: { $size: "$profile.handshake" } }
      }
    },
    { $sort: { count: -1 } },
    { $limit: 20 },
    {
      $lookup: {
        from: "users",
        let: {
          username: "$_id",
          idapp,
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  {
                    $eq: [
                      "$$username",
                      "$username",
                    ],
                  },
                  {
                    $eq: [
                      "$$idapp",
                      "$idapp",
                    ],
                  },
                ],
              },
            },
          },
        ],
        as: "user",
      },
    },
    { $unwind: "$user" },
    {
      $replaceRoot: {
        newRoot: {
          $mergeObjects: ["$user", "$$ROOT"],
        },
      },
    },
    {
      $project: {
        _id: 0,
        count: 1,
        aportador_solidario: 1,
        username: 1,
        name: 1,
        surname: 1,
        lasttimeonline: 1,
        date_reg: 1,
        idapp: 1,
        "profile.img": 1,
        'profile.handshake': 1,
        'profile.mycircuits': 1,
      },
    },
  ];
  return query;
};

UserSchema.statics.getQueryReceiveRISUsers = function (idapp, hours) {

  const query = [
    {
      $match: {
        idapp,
        'profile.lastdate_reqRis': { $gte: tools.IncDateNow(-(1000 * 60 * 60 * hours)) },
        $or: [
          { deleted: { $exists: false } },
          { deleted: { $exists: true, $eq: false } }],
      },
    },
    {
      $group:
      {
        _id: "$username",
        count: {
          $sum: 1,
        },
      }
    },
    { $sort: { 'profile.lastdate_reqRis': -1 } },
    { $limit: 30 },
    {
      $lookup: {
        from: "users",
        let: {
          username: "$_id",
          idapp,
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  {
                    $eq: [
                      "$$username",
                      "$username",
                    ],
                  },
                  {
                    $eq: [
                      "$$idapp",
                      "$idapp",
                    ],
                  },
                ],
              },
            },
          },
        ],
        as: "user",
      },
    },
    { $unwind: "$user" },
    {
      $replaceRoot: {
        newRoot: {
          $mergeObjects: ["$user", "$$ROOT"],
        },
      },
    },
    {
      $project: {
        _id: 0,
        count: 1,
        aportador_solidario: 1,
        username: 1,
        name: 1,
        surname: 1,
        lasttimeonline: 1,
        'profile.lastdate_reqRis': 1,
        'profile.mycircuits': 1,
        date_reg: 1,
        idapp: 1,
        "profile.img": 1,
        'profile.handshake': 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 await User.aggregate(User.getnumRegNDays(idapp, 60)).then((arr) => {
    return arr.reduce((sum, rec) => sum + rec.count, 0);
  });

};

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

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

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

  return await 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({ userId: 1 });
  UserSchema.index({ 'idapp': 1 });


  // UserSchema.index({ username: 'text', name: 'text', surname: 'text', email: 'text' });
  // UserSchema.index({ name: 'name' });
  // UserSchema.index({ name: 1 });
  // UserSchema.index({ surname: 1 });
  // ter

  tools.INITDB_FIRSTIME = false;
}

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.getExtraInfoByUsername = async function (idapp, username) {
  const User = this;

  let myuser = await User.findOne({ idapp, username }).lean();
  if (myuser) {
    myuserextra = await User.addExtraInfo(idapp, myuser, null);
  }

  return myuser.profile;

};
UserSchema.statics.addExtraInfo = async function (idapp, recUser, recUserSave, version) {

  try {
    // tools.startTimeLog('addExtraInfo')

    if (version) {
      let versattualeuser = 0;
      if (!recUser.profile.version) {
        recUser.version = 0;
        versattualeuser = 0;
      } else {
        versattualeuser = recUser.profile.version;
      }

      // versattualeuser = 0;  // TOGLIERE!

      if (versattualeuser < version) {
        // Aggiornamento versione
        recUser = await User.updateVersion(versattualeuser, recUser);

        await User.findOneAndUpdate({ _id: recUser._id }, { $set: { 'profile.version': version } });
      }
    }

    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()).lean();

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

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

    recUser.profile.refused_groups = listRefusedGroups
      ? listRefusedGroups
      : [];

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

    recUser.profile.reaction = await Reaction.find({ idapp, username: recUser.username }).lean();

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

    // Circuit>

    const circuitobj = await Circuit.getCircuitsByUsername(idapp, recUser.username, recUser);

    const useraccounts = await Account.getUserAccounts(idapp, recUser.username);

    for (const group of listManageGroups) {
      const myaccounts = await Account.getGroupAccounts(idapp, group.groupname);
      if (myaccounts && myaccounts.length > 0)
        group.account = myaccounts[0];
      else
        group.account = null;
    }

    recUser.profile = { ...recUser.profile, ...circuitobj, useraccounts };

    recUser.calcstat = await User.calculateStat(idapp, recUser.username);

    recUser.profile.calc = await User.calcOtherByUser(idapp, recUser._id);

    // tools.endTimeLog('addExtraInfo')

    return recUser;

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

  return recUser;
};

addRecordReaction = async function (arr, fields, fieldtoset) {
  let newrecord = {};

  try {

    if (tools.isArray(arr)) {
      for (let myrec of arr) {
        if (myrec.id) {
          let myfields = { ...{ idrec: myrec.id }, ...fields };
          newrecord = await Reaction.findOne(myfields)

          if (!newrecord) {
            newrecord = new Reaction(myfields);
            // console.log('Nuovo Record: ', newrecord._doc);
          }

          newrecord[fieldtoset] = true;

          await newrecord.save();

          let newrecfound = await Reaction.findOne(myfields)
        }
        // if (newrecfound)
        //   console.log('trovato')
      }
    }

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

};


UserSchema.statics.moverecordsFavorite = async function (tab) {
  const { MySkill } = require('../models/myskill');
  const { MyGood } = require('../models/mygood');

  console.log('INIZIO - moverecordsFavorite tab', tab);

  const arrusers = await User.find({ idapp: '13' }).lean();

  let index = 0;
  try {


    let index = 0;
    let totali = 0;
    for (let user of arrusers) {

      if (user.profile) {
        let arrfav = user.profile.favorite;
        let arrseen = user.profile.seen;
        let arrbookmark = user.profile.bookmark;
        let arrattend = user.profile.attend;

        totali++;

        if (arrfav || arrseen) {
          console.log('utente n.', index, 'su', totali, user.username);
          index++;

          let fields = {
            idapp: user.idapp,
            userId: user._id,
            username: user.username,
            tab
          };

          await addRecordReaction(arrfav, fields, 'fav');
          await addRecordReaction(arrseen, fields, 'seen');
          await addRecordReaction(arrbookmark, fields, 'book:');
          await addRecordReaction(arrattend, fields, 'attend');
        }

      }

    }

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

  console.log('fine moverecordsFavorite');

};

UserSchema.statics.removerecordsFavorite = async function () {
  // Rimuovi i record del vecchio schema
  const attivacanc = true;

  if (attivacanc) {
    const queryfind = { idapp: '13' };
    await User.findOneAndUpdate(queryfind, {
      $set:
      {
        'profile.favorite': [],
        'profile.bookmark': [],
        'profile.attend': [],
        'profile.seen': []
      },
    });

  }
};

UserSchema.statics.updateVersion = async function (userversion, recUser) {

  const { MySkill } = require('../models/myskill');
  const { MyGood } = require('../models/mygood');
  const { City } = require('../models/city');

  if (userversion < 604) {
    if (!recUser.profile.notifs || recUser.profile.notifs.length === 0) {
      recUser.profile.notifs = shared_consts.DEFAULT_NOTIFS_USER;
    }

    const recfavoriteNotif = recUser.profile.notifs.find((rec) => rec.dir === shared_consts.TypeNotifs.TYPEDIR_FAVORITE);
    if (!recfavoriteNotif) {
      recUser.profile.notifs.push({ dir: shared_consts.TypeNotifs.TYPEDIR_FAVORITE, value: shared_consts.TypeNotifs.ID_FAVORITE_ADDED })
    }

    const recAttendNotif = recUser.profile.notifs.find((rec) => rec.dir === shared_consts.TypeNotifs.TYPEDIR_EVENTS);
    if (recAttendNotif) {
      if (!tools.isBitActive(recAttendNotif.value, shared_consts.TypeNotifs.ID_EVENTS_ATTEND)) {
        recAttendNotif.value = tools.SetBit(recAttendNotif.value, shared_consts.TypeNotifs.ID_EVENTS_ATTEND);
      }

      for (let i = 0; i < recUser.profile.notifs.length; i++) {
        if (recUser.profile.notifs[i].dir === shared_consts.TypeNotifs.TYPEDIR_EVENTS)
          recUser.profile.notifs[i].value = recAttendNotif.value;
      }

    }

    // Imposta la provincia, se non l'ho messa, in base al bene o servizio che hai messo.
    if (!recUser.profile.resid_province) {
      let resid_province = '';
      const firstrec = await MySkill.findOne({ userId: recUser._id }).lean();
      if (firstrec && firstrec.idCity && firstrec.idCity.length > 0) {
        resid_province = await City.getProvinceByIdCity(firstrec.idCity[0]);
      } else {
        const firstrec = await MyGood.findOne({ userId: recUser._id }).lean();
        if (firstrec && firstrec.idCity && firstrec.idCity.length === 1) {
          resid_province = await City.getProvinceByIdCity(firstrec.idCity[0]);
        }
      }


      if (resid_province) {
        await User.findOneAndUpdate({ _id: recUser._id }, { $set: { 'profile.resid_province': resid_province } });
      }
    }

    let recmybach = recUser.profile.notifs.find((rec) => rec.dir === shared_consts.TypeNotifs.TYPEDIR_BACHECA);
    if (!tools.isBitActive(recmybach.value, shared_consts.UsersNotif.NEW_ADV_YOUR_PROVINCE)) {
      recmybach.value = tools.SetBit(recmybach.value, shared_consts.UsersNotif.NEW_ADV_YOUR_PROVINCE);
      for (let i = 0; i < recUser.profile.notifs.length; i++) {
        if (recUser.profile.notifs[i].dir === shared_consts.TypeNotifs.TYPEDIR_BACHECA)
          recUser.profile.notifs[i].value = recmybach.value;
      }
    }

    await User.findOneAndUpdate({ _id: recUser._id }, { $set: { 'profile.notifs': recUser.profile.notifs } });
  }

  if (userversion < 1016) {

  }

  return recUser;
};

UserSchema.statics.calcOtherByUser = async function (idapp, userId) {

  const { MySkill } = require('../models/myskill');
  const { MyGood } = require('../models/mygood');
  const { MyHosp } = require('../models/myhosp');

  let calc = {};

  let numgoods = await MyGood.countDocuments({ idapp, userId });
  let numskills = await MySkill.countDocuments({ idapp, userId });
  let numhosps = await MyHosp.countDocuments({ idapp, userId });

  calc.numGoodsAndServices = numgoods + numskills + numhosps;

  return calc;
};


UserSchema.statics.tooManyReqPassword = async function (idapp, email, set) {
  const User = this;

  const maxnum = 30;

  const user = await User.findByEmail(idapp, email);
  if (user) {
    if (!user.retry_pwd)
      user.retry_pwd = 0
    if (set && user.retry_pwd <= maxnum) {
      user.retry_pwd++;

      await User.findOneAndUpdate({ _id: user._id }, { $set: { retry_pwd: user.retry_pwd } });
    }
    return user.retry_pwd > maxnum;
  }

};

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

  try {
    return await User.findOneAndUpdate({ idapp, username }, { $set: { 'profile.last_circuitpath': circuitpath } });
  } catch (e) {
    return false;
  }

};

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

  return await User.findOneAndUpdate({
    idapp, username,
  },
    { $set: { 'profile.lastdate_reqRis': new Date() } }, { new: false }).lean().then((record) => {
      return !!record;
    });

};

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.`;
  }
}

const FuncUsers = {
  createRegistration_withTelegram(userdata) {

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

    try {
      // Cerca se esiste già
      const user = new User(userdata);

      user.verified_email = false;
      user.lasttimeonline = new Date();
      user.date_reg = new Date();

      return user.save().then(async () => {
        return User.findByUsername(user.idapp, user.username, false).
          then(async (usertrovato) => {

            const numutenti = await User.getNumUsers(user.idapp);

            let msg = '++ Nuovo Entrato: [' + numutenti + '] ' + user.username + ' ' + user.name + ' ' + user.surname;

            await telegrambot.sendMsgTelegramToTheManagers(user.idapp, msg);

            return telegrambot.askConfirmationUser(user.idapp, shared_consts.CallFunz.REGISTRATION, user, '', '', '', '');
          });
      }).catch((e) => {
        console.error(e.message);
      });

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

  }
};


module.exports = {
  User, Hero, FuncUsers
};


