const mongoose = require('mongoose').set('debug', false);
const Schema = mongoose.Schema;

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

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

mongoose.Promise = global.Promise;
mongoose.level = 'F';

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

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

const MyGroupSchema = new Schema({
  _id: {
    type: String,
    default: function () {
      return new ObjectId().toString();
    },
  },
  idapp: {
    type: String,
  },
  groupname: {
    type: String,
  },
  title: {
    type: String,
  },
  descr: {
    type: String,
  },
  idCatGrp: [
    {
      type: Number,
    },
  ],
  userId: {
    type: String,
  },
  photos: [
    {
      imagefile: {
        type: String,
      },
      alt: {
        type: String,
      },
      description: {
        type: String,
      },
    },
  ],
  idCity: [
    {
      type: Number,
    },
  ],
  website: {
    type: String,
  },
  link_telegram: {
    type: String,
  },
  note: {
    type: String,
    default: '',
  },
  visibility: [
    {
      type: Number,
    },
  ],
  pwd_cryp: {
    type: String,
  },
  createdBy: {
    type: String,
  },
  admins: [
    {
      username: { type: String },
      perm: { type: Number },
      date: { type: Date },
    },
  ],
  blocked: {
    type: Boolean,
  },
  username_who_block: {
    type: String,
  },
  date_created: {
    type: Date,
    default: Date.now,
  },
  date_updated: {
    type: Date,
  },
  date_blocked: {
    type: Date,
  },
  req_users: [
    {
      _id: false,
      username: { type: String },
      date: { type: Date },
    },
  ], // username
  refused_users: [
    {
      _id: false,
      username: { type: String },
      date: { type: Date },
    },
  ], // username
  deleted: {
    type: Boolean,
    default: false,
  },
  mycircuits: [
    {
      _id: false,
      circuitname: { type: String },
      date: { type: Date },
    },
  ],
  lastdate_reqRisGroup: {
    type: Date,
  },
  //**ADDFIELD_MYGROUPS

  ...tools.getFieldsForAnnunci(),
});

MyGroupSchema.statics.getFieldsForSearch = function () {
  return [
    { field: 'descr', type: tools.FieldType.string },
    { field: 'groupname', type: tools.FieldType.string },
    { field: 'title', type: tools.FieldType.string },
  ];
};

MyGroupSchema.statics.executeQueryTable = function (idapp, params, user) {
  params.fieldsearch = this.getFieldsForSearch();

  const { User } = require('./user');

  /*if (params.options) {
    if (tools.isBitActive(params.options, shared_consts.OPTIONS_SEARCH_USER_ONLY_FULL_WORDS)) {
      params.fieldsearch = User.getFieldsForSearchUserFriend();
    } else if (tools.isBitActive(params.options, shared_consts.OPTIONS_SEARCH_USER_ALL_WORDS)) {
      params.fieldsearch = User.getFieldsForSearchUserFriend_AllWords();
    }
  }*/

  return tools.executeQueryTable(this, idapp, params, user);
};

MyGroupSchema.pre('save', async function (next) {
  if (this.isNew) {
    if (!this.date_created) this.date_created = new Date();
  }

  next();
});

MyGroupSchema.statics.findAllIdApp = async function (idapp) {
  const myfind = { idapp };

  return await MyGroup.find(myfind).lean();
};

MyGroupSchema.statics.findAllGroups = async function (idapp) {
  const whatToShow = this.getWhatToShow(idapp, '');

  return await MyGroup.find(
    {
      idapp,
      $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
    },
    whatToShow
  ).lean();
};

// Rimuovo la Richiesta del Gruppo
MyGroupSchema.statics.removeReqGroup = async function (idapp, username, groupnameDest) {
  return await MyGroup.updateOne(
    { idapp, groupname: groupnameDest },
    { $pull: { req_users: { username: { $in: [username] } } } }
  );
};

// Aggiungi agli utenti Rifiutati del Gruppo
MyGroupSchema.statics.refuseReqGroup = async function (idapp, username, groupnameDest) {
  return await MyGroup.updateOne(
    { idapp, groupname: groupnameDest },
    {
      $push: {
        refused_users: {
          username,
          date: new Date(),
        },
      },
    }
  );
};

// Aggiungi agli Admin del Gruppo
MyGroupSchema.statics.addToAdminOfMyGroup = async function (idapp, username, groupnameDest) {
  return await MyGroup.updateOne(
    { idapp, groupname: groupnameDest },
    {
      $push: {
        admins: {
          username,
          date: new Date(),
        },
      },
    }
  );
};

// Rimuovi dagli Admin del Gruppo
MyGroupSchema.statics.removeAdminOfMyGroup = async function (idapp, username, groupnameDest) {
  return await MyGroup.updateOne(
    { idapp, groupname: groupnameDest },
    { $pull: { admins: { username: { $in: [username] } } } }
  );
};

MyGroupSchema.statics.getListAdminsByGroupName = async function (idapp, groupname) {
  let arr = await MyGroup.findOne(
    {
      idapp,
      groupname,
      $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
    },
    { admins: 1 }
  ).lean();

  return arr && arr.admins ? arr.admins : [];
};

MyGroupSchema.statics.getWhatToShow = function (idapp, username) {
  // FOR ME, PERMIT ALL
  let whatToShow = {
    groupname: 1,
    title: 1,
    descr: 1,
    visibility: 1,
    idCatGrp: 1,
    userId: 1,
    photos: 1,
    idCity: 1,
    website: 1,
    link_telegram: 1,
    note: 1,
    da_contattare: 1,
    admins: 1,
    blocked: 1,
    req_users: 1,
    refused_users: 1,
    createdBy: 1,
    date_created: 1,
    date_updated: 1,
    mycircuits: 1,
    lastdate_reqRisGroup: 1,
    //**ADDFIELD_MYGROUPS
  };

  whatToShow = { ...whatToShow, ...shared_consts.ANNUNCI_FIELDS };

  return whatToShow;
};

MyGroupSchema.statics.getWhatToShow_Unknown = function (idapp, username) {
  let whatToShow = {
    groupname: 1,
    title: 1,
    descr: 1,
    photos: 1,
    visibility: 1,
    idCatGrp: 1,
    idCity: 1,
    note: 1,
    date_created: 1,
    date_updated: 1,
    mycircuits: 1,
    lastdate_reqRisGroup: 1,
  };

  whatToShow = { ...whatToShow, ...shared_consts.ANNUNCI_FIELDS };

  return whatToShow;
};

MyGroupSchema.statics.getArrUsernameFromFieldByGroupname = async function (idapp, groupname, field) {
  const { User } = require('../models/user');

  const myobj = {};
  myobj[field] = 1;

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

  if (ris) {
    return ris[field].map((m) => m.username);
  }
  return [];
};

MyGroupSchema.statics.getInfoGroupByGroupname = async function (idapp, groupname) {
  const whatToShow = this.getWhatToShow(idapp, groupname);

  const myfind = {
    idapp,
    groupname,
  };

  const query = [
    { $match: myfind },
    {
      $lookup: {
        from: 'circuits',
        localField: 'mycircuits.circuitname',
        foreignField: 'name',
        as: 'mycircuits',
      },
    },
    {
      $replaceRoot: {
        newRoot: {
          $mergeObjects: [
            {
              $arrayElemAt: ['$mycircuits', 0],
            },
            '$$ROOT',
          ],
        },
      },
    },
    { $project: whatToShow },
    {
      $lookup: {
        from: 'catgrps',
        localField: 'idCatGrp',
        foreignField: '_id',
        as: 'recCatGrp',
      },
    },
    {
      $project: shared_consts.getProjectByTable(shared_consts.TABLES_MYGROUPS, {}),
    },
  ];

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

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

  return null;
};

MyGroupSchema.statics.deleteGroup = async function (idapp, usernameOrig, groupname) {
  console.log('Gruppo ' + groupname + ' rimosso da ' + usernameOrig);
  return await MyGroup.findOneAndDelete({ idapp, groupname });
};

MyGroupSchema.statics.getGroupsByUsername = async function (idapp, username, req) {
  try {
    const { User } = require('../models/user');

    const whatToShow = this.getWhatToShow(idapp, username);
    const whatToShow_Unknown = this.getWhatToShow_Unknown(idapp, username);
    const arrUsernameGroups = await User.getUsernameGroupsByUsername(idapp, username);
    // const arrUsernameReqGroups = await MyGroup.getUsernameReqGroupsByGroupname(idapp, username);

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

    let listgroups = await MyGroup.find(
      {
        idapp,
        $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
      },
      whatToShow_Unknown
    );

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

     */

    let listSentRequestGroups = await MyGroup.find(
      {
        idapp,
        req_users: {
          $elemMatch: { username: { $eq: username } },
        },
        $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
      },
      whatToShow_Unknown
    );

    let listRefusedGroups = await MyGroup.find(
      {
        idapp,
        refused_users: {
          $elemMatch: { username: { $eq: username } },
        },
        $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
      },
      whatToShow_Unknown
    );

    return {
      listUsersGroup,
      listgroups,
      listSentRequestGroups,
      listRefusedGroups,
      mygroups: await User.getMyGroupsById(req.user._id),
    };
  } catch (e) {
    console.log('Error', e);
  }

  return {
    listUsersGroup: [],
    listRequestUsersGroup: [],
    listTrusted: [],
    listSentRequestGroups: [],
    listRefusedGroups: [],
  };
};

MyGroupSchema.statics.extractCitiesName = async function (idapp, id) {
  try {
    let aggr1 = [
      {
        $match: { idapp, _id: id },
      },
      {
        $lookup: {
          from: 'cities',
          localField: 'idCity',
          foreignField: '_id',
          as: 'mycities',
        },
      },
      {
        $replaceRoot: {
          newRoot: {
            $mergeObjects: [
              {
                $arrayElemAt: ['$mycities', 0],
              },
              '$$ROOT',
            ],
          },
        },
      },
      {
        $project: {
          'mycities.comune': 1,
          'mycities.prov': 1,
        },
      },
    ];

    ris = await this.aggregate(aggr1);

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

MyGroupSchema.statics.ifCircuitAlreadyInGroup = async function (idapp, groupname, circuitname) {
  // Controllo se è stato già inserito il circuito sul gruppo
  return await this.findOne({
    idapp,
    groupname,
    mycircuits: {
      $elemMatch: { circuitname: { $eq: circuitname } },
    },
  }).lean();
};

// aggiungo il Circuito all'interno del Gruppo
MyGroupSchema.statics.addCircuitFromGroup = async function (idapp, groupname, circuitname) {
  return await this.updateOne(
    { idapp, groupname },
    {
      $push: {
        mycircuits: {
          circuitname,
          date: new Date(),
        },
      },
    }
  );
};

// Rimuovo il Circuito all'interno del Gruppo
MyGroupSchema.statics.removeCircuitFromGroup = async function (idapp, groupname, circuitname) {
  const { Circuit } = require('../models/circuit');

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

  const ris = await this.updateOne(
    { idapp, groupname },
    { $pull: { 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, '', circuitId, false, false, groupname, '');
  if (myaccount && myaccount.totTransato === 0) {
    remove = true;
  } else {
    remove = true;
  }

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

MyGroupSchema.statics.getQueryReceiveRISGroups = function (idapp, hours) {
  const query = [
    {
      $match: {
        idapp,
        lastdate_reqRisGroup: { $gte: tools.IncDateNow(-(1000 * 60 * 60 * hours)) },
        $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
      },
    },
    {
      $group: {
        _id: '$groupname',
        count: {
          $sum: 1,
        },
      },
    },
    { $sort: { lastdate_reqRisGroup: -1 } },
    { $limit: 30 },
    {
      $lookup: {
        from: 'mygroups',
        let: {
          groupname: '$_id',
          idapp,
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  {
                    $eq: ['$$groupname', '$groupname'],
                  },
                  {
                    $eq: ['$$idapp', '$idapp'],
                  },
                ],
              },
            },
          },
        ],
        as: 'mygroup',
      },
    },
    { $unwind: '$mygroup' },
    {
      $replaceRoot: {
        newRoot: {
          $mergeObjects: ['$mygroup', '$$ROOT'],
        },
      },
    },
    {
      $project: {
        _id: 0,
        groupname: 1,
        title: 1,
        descr: 1,
        visibility: 1,
        idCatGrp: 1,
        userId: 1,
        photos: 1,
        idCity: 1,
        website: 1,
        link_telegram: 1,
        note: 1,
        admins: 1,
        blocked: 1,
        req_users: 1,
        createdBy: 1,
        date_created: 1,
        date_updated: 1,
        lastdate_reqRisGroup: 1,
      },
    },
  ];
  return query;
};

MyGroupSchema.statics.getReceiveRISGroups = async function (idapp) {
  return await this.aggregate(this.getQueryReceiveRISGroups(idapp, 8)).then((ris) => {
    return ris;
  });
};

MyGroupSchema.statics.renameCircuitName = async function (idapp, oldcircuitname, newcircuitname) {
  return await this.updateMany(
    { idapp, 'mycircuits.circuitname': oldcircuitname },
    { $set: { 'profile.mycircuits.$.circuitname': newcircuitname } }
  );
};

MyGroupSchema.statics.setReceiveRisGroup = async function (idapp, groupname) {
  const record = await this.findOneAndUpdate(
    { idapp, groupname },
    { $set: { lastdate_reqRisGroup: new Date() } },
    { new: false }
  ).lean();

  return !!record; // Restituisce true se il record esiste, false altrimenti
};

const MyGroup = mongoose.model('MyGroup', MyGroupSchema);

MyGroup.createIndexes()
  .then(() => {})
  .catch((err) => {
    throw err;
  });

module.exports = { MyGroup };
